#!/usr/bin/python
# encoding: utf-8

"""
@file: common_opers.py
@time: 16/8/25 下午9:10
"""
from functools import reduce
from itertools import combinations, chain
from collections import OrderedDict
from collections.abc import Iterable
import uuid
from mathsolver.functions.base import *
from sympy import (sympify, simplify, factor, cancel, expand, collect, sqrt, Add, fraction, S, symbols, sqrtdenest,
                   powsimp, Pow, degree, degree_list, Mul, logcombine, log, solve_poly_system, Abs, pi, lcm, gcd,
                   solveset)
from mathsolver.functions.root.jiefangchenzu import JieFangChenZu
from sympy.sets.fancysets import Reals
from sympy.core.numbers import NaN
from mathsolver.functions.mathematica.mathematicaSolve import MathematicaSolve, MathematicaSimplify

_latters = [chr(ord('a') + i) for i in range(0, 26)]  # 26 个字母


def is_algebraic_expr(obj):
    """
    对象是否是代数表达式
    :param obj: 
    :return: 
    """
    return hasattr(obj, 'is_algebraic_expr') and obj.is_algebraic_expr()


def is_abs_expr(e):
    """
    是否是绝对值表达式
    :param e: 
    :return: 
    """
    if str(e).find('Abs') >= 0:
        return True
    return False


def iterable(obj):
    """
    判断对象是否可迭代
    :param obj:
    :return:
    """
    return not isinstance(obj, str) and isinstance(obj, Iterable)


def i_gcd(*mons):
    """
    求多个式子的最大公约数
    :param mons: 
    :return: 
    >>> i_gcd(12, 6, 9)
    3
    >>> i_gcd('2*a*x', '2*a', '5*a')
    a
    """
    _gcd = reduce(gcd, mons)
    return _gcd


def n_grams(a, nn, step=1):
    """
    滑动窗口取值
    :param a: 列表
    :param nn: 窗口大小
    :param step: 每次步长
    :return:
    >>> n_grams([1, 2, 3, 4, 5, 6], 3)
    [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]]
    >>> n_grams([1, 2, 3, 4, 5, 6], 3, 3)
    [[1, 2, 3], [4, 5, 6]]
    >>> n_grams([1, 2, 3, 4, 5, 6], 8, 3)
    [[1, 2, 3, 4, 5, 6]]
    """
    len_a = len(a)
    result = []
    for index in range(0, len_a, step):
        result.append(a[index:index + nn])
        if index + nn >= len_a:
            break
    return result


def symoms_and_const(e, x_symbol=None):
    """
    变量项和常量
    :param e:
    :param x_symbol: 
    :return:
    >>> symoms_and_const('3*a + 5*b - 6')
    ([5*b, 3*a], -6)
    >>> symoms_and_const('3*sin(x) - 5*sin(2*x - 7) - 10')
    ([-5*sin(2*x - 7), 3*sin(x)], -10)
    """
    e = expand(sympify(e))
    if e.is_real:
        return None, e
    if not x_symbol:
        mons = list(map(lambda t: t[0] * t[1], e.as_coefficients_dict().items()))
        sym_mons = list(filter(lambda mon: not mon.is_real, mons))
        cont = e - reduce(lambda m1, m2: m1 + m2, sym_mons)
        return sym_mons, cont
    else:
        cont, x_f = e.as_independent(x_symbol)
        return map(lambda t: t[0] * t[1], x_f.as_coefficients_dict().items()), cont


def mo_coeff(mo):
    """
    单项式系数
    :param mo: 单项式
    :return:
    >>> mo_coeff('x')
    1
    """
    mo = sympify(mo)
    args = mo.args
    if not args:
        args = (sympify('1'),)
    mo_args = filter(lambda a: a.is_real, args)
    if not mo_args:
        return sympify('1')
    return reduce(lambda a, b: a * b, mo_args)


def coefficients_dict(f):
    """
    sympy自带的as_coefficients_dict()有问题.
    :param f:
    :return:
    >>> coefficients_dict('5*x -1/3*x**2 + 5')
    {1: 5, x: 5, x**2: -1/3}
    >>> coefficients_dict('sqrt(3)*x - 5*x**2')
    {x: sqrt(3), x**2: -5}
    """
    d = sympify(f).as_coefficients_dict()
    cd = {}
    for mo, coef in d.items():
        if str(mo).find('sqrt') >= 0:  # isinstance(mo, Mul)
            mc = mo_coeff(mo)
            mo = mo / mc
            coef = coef * mc
        cd[mo] = coef
    return cd


def x_y_symbols(e):
    """
    提取表达式中约定的x, y符号. 默认为x, y
    :param e: 表达式
    :return: 表示x, y的符号
    >>> x_y_symbols('3*b - 5*a')
    (a, b)
    """
    e = sympify(e)
    symbs = list(sorted(map(str, e.free_symbols)))
    # -------------------------
    if 'x' in symbs and 'y' in symbs:
        return 'x', 'y'
    # ---------------------------
    x_symbol = symbs[0] if str(symbs[0]) != 'y' else 'x'
    y_symbol = symbs[1] if len(symbs) >= 2 else 'y'
    return x_symbol, y_symbol


def reverse_ineq_op(ineq_op):
    """
    不等式运算符反向
    :param ineq_op: 不等式运算符
    :return:
    >>> reverse_ineq_op('>=')
    <=
    >>> reverse_ineq_op('<=')
    >=
    """
    if ineq_op not in ['>=', '>', '<=', '<']:
        raise Exception('illegal ineq operate')
    return ineq_op.replace('>', '<') if ineq_op.find('>') >= 0 else ineq_op.replace('<', '>')


def eq_2_f(eq):
    """
    等式转变为f(x)的表达式
    :param eq: 等式
    :return:
    >>> eq_2_f(BaseEq(['3*x/y + x', '3']))
    x*y + 3*x - 3*y
    """
    left, right = eq.sympify()
    left_frac_mo, left_frac_de = fraction(cancel(left))
    right_frac_mo, right_frac_de = fraction(cancel(right))
    f = left_frac_mo * right_frac_de - left_frac_de * right_frac_mo
    return f


def absed_mo(e):
    """
    表达式是否是含有绝对值的项
    :param e:
    :return:
    """
    p = r'Abs\(.*?\)'
    return re.findall(p, str(sympify(e)))


def absed_mo_v2(e):
    """
    表达式是否是含有绝对值的项
    :param e:
    :return:
    >>> absed_mo_v2('Abs(sin(pi*x/2)) + Abs(cos(pi*x/2))')
    ['Abs(sin(pi*x/2))', 'Abs(cos(pi*x/2))']
    >>> absed_mo_v2('Abs(2*x + 3)')
    ['Abs(2*x + 3)']
    ['Abs(sin(pi*x/2))', 'Abs(cos(pi*x/2))']
    >>> absed_mo_v2('Abs(2*x + sin(3*x + 2)) + Abs(2*x - 3) + 5')
    ['Abs(2*x + sin(3*x + 2))', 'Abs(2*x - 3)']
    """
    e = sympify(e)
    d = coefficients_dict(e)
    absed_mos = []
    p = r'Abs\(.*\)'
    for m, c in d.items():
        if re.findall(p, str(m)):
            absed_mos.extend(re.findall(p, str(m)))
    return absed_mos


def find_reg_expr(reg, expr, deep=False):
    """
    查找正则表达式所匹配的表达式
    :param reg: 正则
    :param expr: 表达式
    :param deep: 是否深度搜索
    :return:
    """
    expr = str(expr)
    pattern = reg
    expr_list = re.findall(pattern, expr)
    exprs = []
    start_find = 0

    def able_expr(full_expr, start_expr, start_i):
        full_expr = str(full_expr)
        start_expr = str(start_expr)
        end_i = full_expr.find(start_expr, start_i) + len(start_expr)
        expr_size = len(full_expr)
        while end_i <= expr_size:
            last_expr = full_expr[start_i:end_i]
            try:
                sympify(last_expr)
                if re.match(pattern, last_expr):
                    break
                else:
                    continue
            except Exception:
                pass
            end_i += 1
        return last_expr

    for tmp_expr in expr_list:
        start_find = expr.find(tmp_expr, start_find)
        current_expr = able_expr(expr, tmp_expr, start_find)
        if deep:
            if re.findall(pattern, str(current_expr)[1:]):
                exprs.append(str(current_expr))
                exprs.extend(find_reg_expr(reg, str(current_expr)[1:]))
            else:
                exprs.append(str(current_expr))
        else:
            exprs.append(str(current_expr))
        start_find += len(str(current_expr))
    exprs = list(filter(lambda e: e.find(pattern[:2]) >= 0, exprs))
    return exprs


def absed_term_coeff(absed_e):
    """
    绝对值项的绝对值部分和系数
    :param absed_e:
    :return:
    >>> absed_term_coeff('-5*Abs(3*x - 6)')
    (Abs(3*x - 6), -5)
    >>> absed_term_coeff('-m*5*Abs(3*x - 6)')
    (m*Abs(3*x - 6), -5)
    """
    p = r'Abs\(.*?\)'
    mos = re.findall(p, str(sympify(absed_e)))
    if mos:
        m = sympify(mos[0])
        c = sympify(absed_e) / m
        if c.is_real:
            return m, c
        else:
            cs, cc = list(c.as_coefficients_dict().items())[0]
            return m * cs, cc
    return 0, 0


def simp_absed_ineq(absed_ineq):
    """
    化简含有绝对值表达式的不等式, 友好的绝对值不等式
    :param absed_ineq: 含有绝对值的不等式
    :return: 绝对值在左边, 其他的在右边
    >>> simp_absed_ineq(BaseIneq(['6', '>=', '3*x - Abs(2*y)'])).sympify()
    (2*Abs(y), '>=', 3*x - 6)
    >>> simp_absed_ineq(BaseIneq(['3*x + 5', '>=', '2*y'])).sympify()
    (3*x + 5, '>=', 2*y)
    """
    if iterable(absed_ineq) and len(absed_ineq) == 3:
        absed_ineq = BaseIneq(absed_ineq)
    left, ineq_op, right = absed_ineq.sympify()
    f = left - right
    absed_terms = []
    for k, v in f.as_coefficients_dict().items():
        if isinstance(k, Abs):
            absed_terms.append(k * v)
    if absed_terms:
        a = sympify(absed_terms[0])
        k, v = list(a.as_coefficients_dict().items())[0]
        if v.is_real:
            pm = v / abs(v)
            new_left = a / pm
            new_right = -(f - a) / pm
            if v > 0:
                return BaseIneq([new_left, ineq_op, new_right])
            else:
                return BaseIneq([new_left, reverse_ineq_op(ineq_op), new_right])
        else:
            new_left = a
            new_right = -(f - a)
            return BaseIneq([new_left, ineq_op, new_right])
    else:
        return absed_ineq


def simp_radical_ineq(ineq):
    """
    化简含有根号的不等式
    :param ineq: 含有根号的不等式
    :return:
    """
    left, ineq_op, right = ineq.sympify()
    f = left - right
    radical_terms = []
    for k, v in f.as_coefficients_dict().items():
        if isinstance(k, Pow):
            radical_terms.append(k * v)
    if radical_terms:
        a = sympify(radical_terms[0])
        k, v = list(a.as_coefficients_dict().items())[0]
        if v.is_real:
            pm = v / abs(v)
            new_left = a / pm
            new_right = -(f - a) / pm
            if v > 0:
                return BaseIneq([new_left, ineq_op, new_right])
            else:
                return BaseIneq([new_left, reverse_ineq_op(ineq_op), new_right])
        else:
            new_left = a
            new_right = -(f - a)
            return BaseIneq([new_left, ineq_op, new_right])
    else:
        return ineq


def simp_ineqs(src_ineqs):
    """
    :param src_ineqs: 不等式组
    :return:
    >>> simp_ineqs(BaseIneqs([['y', '>=', '2*Abs(x)-1'], ['y', '<=', 'x + 1']])).sympify()
    [(2*Abs(x), '<=', y + 1), (2*Abs(x), '>=', -y - 1), (y, '<=', x + 1)]
    """
    target_ineqs_list = []
    new_ineq_list = []
    try:
        ineqs_list = src_ineqs.sympify()
        if iterable(ineqs_list[0][0]) and len(list(flatten(ineqs_list))) % 3 == 0:
            ineqs_list = n_grams(list(flatten(ineqs_list)), 3, 3)
        for tmp_ineq in ineqs_list:
            if iterable(tmp_ineq[0]):
                new_ineq_list.extend(tmp_ineq)
            else:
                new_ineq_list.append(tmp_ineq)
    except Exception:
        pass
    ineqs_list = new_ineq_list
    for ineq in ineqs_list:
        if len(ineq) == 5:  # 形如 3 <= x <= 5的形式
            target_ineqs_list.append([ineq[2], reverse_ineq_op(ineq[1]), ineq[0]])
            # target_ineqs_list.append([ineq[2], reverse_ineq_op(ineq[3]), ineq[4]])  #:old
            target_ineqs_list.append([ineq[2], ineq[3], ineq[4]])
        else:
            if len(ineq) == 2:  # [(-1, '<=', x + y), (x + y, '<=', 4)]
                pass
            ineq = simp_absed_ineq(ineq)
            # left, ineq_op, right = BaseIneq(ineq).sympify()  # 约定left表示表达式, right表示常数(如果不是，则变换一下方向)
            left, ineq_op, right = ineq.sympify()  # 约定left表示表达式, right表示常数(如果不是，则变换一下方向)
            if not absed_mo(left) and left.is_real:
                left, ineq_op, right = right, ineq_op.replace('>', '<') if ineq_op.find(
                    '>') >= 0 else ineq_op.replace('<', '>'), left
            tmp_f = expand(left - right)
            tmp_fac = factor(tmp_f)
            if (len(tmp_fac.args) > 2 or not tmp_fac.args) and not absed_mo(tmp_fac):
                target_ineqs_list.append([left, ineq_op, right])
                continue
            # fac1, fac2 = tmp_fac.args
            if len(tmp_fac.args) == 2 and isinstance(tmp_fac,
                                                     Mul):
                fac1, fac2 = tmp_fac.args
                if not fac1.is_real and not fac2.is_real:  # 形如 (x + y - 5)*(3*x + 6 - 10) >= 0
                    target_ineqs_list.append([fac1, ineq_op, 0])
                    target_ineqs_list.append([fac2, ineq_op, 0])
                else:
                    target_ineqs_list.append([tmp_fac, ineq_op, 0])
            elif absed_mo(left):  # old: isinstance(left, Abs)
                _, left_c = absed_term_coeff(left)
                pm = abs(left_c) / left_c
                new_left = left / pm
                new_right = right / pm
                ineq_op = reverse_ineq_op(ineq_op) if pm < 0 else ineq_op
                n_m, n_c = absed_term_coeff(new_left)
                new_left = n_m.args[0] * n_c
                target_ineqs_list.append([new_left, ineq_op, new_right])
                target_ineqs_list.append([new_left, reverse_ineq_op(ineq_op), -new_right])
            elif absed_mo(right):  # old: isinstance(right, Abs)
                _, right_c = absed_term_coeff(right)
                pm = abs(right_c) / right_c
                new_left = left / pm
                new_right = right / pm
                ineq_op = reverse_ineq_op(ineq_op) if pm < 0 else ineq_op
                new_left, new_right = new_right, new_left
                ineq_op = reverse_ineq_op(ineq_op)
                n_m, n_c = absed_term_coeff(new_left)
                new_left = n_m.args[0] * n_c
                target_ineqs_list.append([new_left, ineq_op, new_right])
                target_ineqs_list.append([new_left, reverse_ineq_op(ineq_op), -new_right])
            else:
                target_ineqs_list.append([left, ineq_op, right])
    return BaseIneqs(target_ineqs_list)


def unique_symbs(symbs, nn):  # 获取 N个不重复的符号变量
    import random
    u_symbs = []
    symb_strs = [str(s) for s in symbs]
    while nn:
        sym = _latters[random.randint(0, 25)]
        if sym not in symb_strs and sym not in u_symbs:
            u_symbs.append(sym)
            nn -= 1
    return u_symbs


def symbol_coeff(sym_e):
    """
    求单项式系数
    :param sym_e: 单项式
    :return: 单项式系数
    >>> symbol_coeff('-2*a')
    -2
    >>> symbol_coeff('2*a')
    2
    """
    s = sympify(sym_e)
    return s.as_coefficients_dict().values()[0]


def sympify_abed_sy(e_str):
    """
    Sympy化包含绝对值符号的表达式
    :param e_str:包含绝对值符号的表达式字符串
    :return: 标准的Sympy表达式
    >>> sympify_abed_sy('a - a/|a + b|')
    a - a/Abs(a + b)
    """
    for item in re.findall('\\|(.*?)\\|', e_str):
        e_str = e_str.replace('|%s|' % item, 'Abs(%s)' % item)
    return sympify(e_str)


def replace_abs_str(e_str):
    """
    替换表达式中的绝对值符号 '|' 为Abs
    :param e_str: 任何包含绝对值符号的表达式
    :return: 替换后的字符串
    >>> replace_abs_str('2 < |x + 1| < 3')
    2 < Abs(x + 1) < 3
    """
    for item in re.findall('\\|(.*?)\\|', e_str):
        e_str = e_str.replace('|%s|' % item, 'Abs(%s)' % item)
    return e_str


def flatten(sequence):
    """
    展开一个嵌套的列表
    :param sequence: 嵌套的列表
    :return: 平铺后的列表
    >>> list(flatten([1, 2, [3, 4], [5, 6, [7, 8, 9], 10]]))
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    """
    iterators = [iter(sequence)]
    while iterators:
        for item in iterators[-1]:
            if isinstance(item, Iterable) and not isinstance(item, str):
                iterators.append(iter(item))
                break
            else:
                yield item
        else:
            iterators.pop()


def nh_zip(seq, nn):
    """
    相邻元素的压缩
    :param seq:
    :param nn:
    :return:
    >>> list(nh_zip([1, 2, 3, 4, 5, 6], 3))
    [(1, 2, 3), (4, 5, 6)]
    """
    return zip(*([iter(seq)] * nn))


def combin_iters(*iters):
    """
    依次从一系列列表中挑选元素进行组合
    :param iters:
    :return:
    >>> combin_iters((1, 2), (3, ))
    [[1, 3], [2, 3]]
    >>> combin_iters((1, 2),(3, 4))
    [[1, 3], [1, 4], [2, 3], [2, 4]]
    >>> combin_iters((1, 2), (3, 4), (5, 6))
    [[1, 3, 5], [1, 3, 6], [1, 4, 5], [1, 4, 6], [2, 3, 5], [2, 3, 6], [2, 4, 5], [2, 4, 6]]
    """

    def not_duped(ps):
        vs = list(map(lambda i: i[1], ps))
        return len(vs) == len(set(vs))

    iters_dict = OrderedDict(map(lambda i: (str(uuid.uuid4()), i), iters))
    items = []
    for k, v in iters_dict.items():
        items.extend(map(lambda i: (i, k), v))
    combins = combinations(items, len(iters))
    combins = filter(not_duped, combins)
    result = []
    for cb in combins:
        result.append(list(map(lambda i: i[0], cb)))
    return result


def expr_symbol(e, sym_str):
    """
    获取表达式中的符号变量, 如果没有则创建一个符号变量
    :param e: 表达式
    :param sym_str: 符号变量名
    :return:
    >>> expr_symbol('3*x + 5*y - 10', 'x')
    x
    """
    expr_symbols = sympify(e).free_symbols
    sym = list(filter(lambda s: str(s) == sym_str, expr_symbols))
    return Symbol(sym_str) if len(sym) == 0 else sym[0]


def interval(start=-S.Infinity, end=S.Infinity, left_open=False, right_open=False):
    """
    创建一个区间对象
    :param start: 最小值
    :param end: 最大值
    :param left_open: 是否包含最小值
    :param right_open: 是否包含最大值
    :return:
    """
    return Interval(start, end, left_open=left_open, right_open=right_open)


def solve_symbol_value(eq, symbol='x'):
    """
    表达目标变量的值
    :param eq: 等式
    :param symbol: 目标变量
    :return:
    >>> solve_symbol_value(BaseEq(['1/a + 1/b', '1']), symbol='b')
    [a/(a - 1)]
    >>> solve_symbol_value(BaseEq(['a +b  + b**2', '1']), symbol='b')
    [-sqrt(-4*a + 5)/2 - 1/2, sqrt(-4*a + 5)/2 - 1/2]
    """
    left, right = eq.sympify()
    symbol = sympify(str(symbol))
    f = left - right
    value = list(solveset(str(f), symbol))
    return value


def to_base_interval(intl):
    """
    将区间转变为BaseInter类型
    :param intl: 区间
    :return:
    >>> to_base_interval(Interval(1, 3).union(Interval(5, 10))).printing()
    \\left[1, 3\\right] \\cup \\left[5, 10\\right]
    """
    if isinstance(intl, Union):
        base_intl_parm = []
        parms = intl.args
        for p in parms:
            left_open = p.left_open
            right_open = p.right_open
            value = [p.left, p.right]
            value.insert(0, '(' if left_open else '[')
            value.append(')' if right_open else ']')
            base_intl_parm.append(value)
            base_intl_parm.append('∪')
        interl = base_intl_parm[:-1]
        return BaseInter(interl)
    elif isinstance(intl, EmptySet):
        pass
    else:
        left_open = intl.left_open
        right_open = intl.right_open
        value = [intl.left, intl.right]
        value.insert(0, '(' if left_open else '[')
        value.append(')' if right_open else ']')
        return BaseInter(value)


def eval_expr(e, v):
    """
    计算单个未知变量的表达式
    :param e:
    :param v:
    :return:
    >>> eval_expr('3*x**2 - 5*x', 3)
    12
    """
    e = sympify(e)
    s = list(e.free_symbols)[0]
    return e.subs(s, v)


# 替换符号变量
# 输入'3*x + 5', (('x', 5), )
def expr_subs(e, symbol_values):
    """
    替换符号变量
    :param e: 表达式
    :param symbol_values: 要替换的 符号值对 列表
    :return: 替换后的表达式
    >>> expr_subs('3*x + 8', ('x', 6))
    26
    >>> expr_subs('3*x + 4*y + 5', (('x', 'z'), ('y', 7)))
    3*z + 33
    """
    symbol_values = symbol_values.items() if isinstance(symbol_values, dict) else symbol_values
    symbol_values = [symbol_values, ] if len(symbol_values) == 2 and (
        isinstance(symbol_values[0], str) or not iterable(symbol_values[0])) else symbol_values
    e = sympify(e)
    symbol_values = map(lambda sv: (expr_symbol(e, str(sv[0])), sv[1]), symbol_values)
    return simplify(e.subs(symbol_values))


def expr_symb_terms_and_others(e, symb='x'):
    """
    将表达式切分为变量项和其他项
    :param e:
    :param symb:
    :return:
    >>> expr_symb_terms_and_others('3*x+ (a*x + 5)*3 + 3*y', 'x')
    (x*(3*a + 3), 3*y + 15)
    """
    symb = str(symb)
    e = collect(sympify(e), symb)
    coefs_dict = coefficients_dict(e)
    symb_terms, others = '0', '0'
    for terms, coeff in coefs_dict.items():
        if str(terms).find(symb) < 0:
            others += ''.join(['+', str(coeff), '*', str(terms)])
        else:
            symb_terms = coeff * terms
    return symb_terms, sympify(others)


def point_2_point_distance(a, b):
    """
    点到点之间的距离
    :param a:
    :param b:
    :return:
    >>> point_2_point_distance((1, 3, 5, 7), (2, 4, 6, 8))
    2
    """
    return sqrt(reduce(Add, map(lambda pair: pow(pair[0] - pair[1], 2), zip(a, b))))


def to_bases(base_list, bases_cls):
    """
    转换成BaseEqs 或者 BaseIneqs
    :param base_list: base列表
    :param bases_cls: BaseEqs 或者 BaseIneqs
    :return:
    >>> to_bases(BaseEqs([['3*x + 2*y', '0'], ['2*x - 3*y', '5']]), BaseEqs)
    >>> to_bases([BaseEq(['3*x + 2*y', '0']), BaseEq(['2*x - 3*y', '5'])], BaseEqs)
    >>> to_bases([['3*x + 2*y', '>', '0'], ['2*x - 3*y', '<=', '5']], BaseIneqs)
    """
    return bases_cls(map(lambda b: b if iterable(b) else b.sympify(), base_list)) if iterable(
        base_list) else base_list


def to_base_list(bases):
    """
    BaseEqs 或者 BaseIneqs 对象转换为其对应的参数列表
    :param bases:
    :return:
    >>> to_base_list(BaseEqs([['3*x + 2*y', '0'], ['2*x - 3*y', '5']]))
    [[3*x + 2*y, 0], [2*x - 3*y, 5]]
    >>> to_base_list([BaseEq(['y', '2*x + 3']), BaseEq(['2*y', '3*x - 6'])])
    [(y, 2*x + 3), (2*y, 3*x - 6)]
    """
    return list(map(lambda b: b if iterable(b) else b.sympify(), bases)) if iterable(bases) else bases.sympify()


def eqs_root(eqs, symbs=None):
    """
    解多元方程组
    :param eqs: 方程组
    :param symbs 变量
    :return:
    >>> eqs_root(BaseEqs([['x + y', '5'], ['x - y', '3']]))
    [[(x, 4), (y, 1)]]
    >>> eqs_root(BaseEqs([['x + y', '5'], ['x - y', '3'], ['z + y', '5']]))
    [[(x, 4), (y, 1), (z, 4)]]
    """
    eqs_list = to_base_list(eqs)
    eq_polys = list(map(lambda eq: sympify(eq[0]) - sympify(eq[1]), eqs_list))
    all_symbs = list(map(lambda ef: ef.free_symbols, eq_polys))
    symbs = symbs if symbs else sorted(set(flatten(all_symbs)), key=str)
    symbs = list(map(sympify, symbs))
    roots = solve_poly_system(eq_polys, *symbs)
    return list(map(lambda root: list(zip(symbs, root)), roots))[0]


def to_base(e, base_cls):
    """
    列表转换成BaseEq或者BaseIneq,如果e是BaseEq或者BaseIneq则不用转换
    :param e:
    :param base_cls:
    :return:
    >>> to_base(['2*x + 3', '0'], BaseEq)
    >>> to_base(BaseEq(['2*x + 3', '0']), BaseEq)
    >>> to_base(['2*x + 3', '>', '0'], BaseIneq).sympify()
    (2*x + 3, '>', 0)
    """
    return e if isinstance(e, base_cls) else base_cls(e)


def line_two_point_eq(point0, point1):
    """
    直线的两点式方程 (x-x0)/(x-x1) = (y-y0)/(y-y1)
    :param point0: 点1
    :param point1: 点2
    :return:
    """
    xx, yy = symbols('x, y')
    line_left = expand((xx - point0[0]) * (yy - point1[1]))
    line_right = expand((xx - point1[0]) * (yy - point0[1]))
    return BaseEq([line_left, line_right])


def line_slope_inter_eq(line_eq, x_symbol='x', y_symbol='y'):
    """
    直线的斜截式形式。如果k为0, 则为y=b; 如果 y = 0,则为0 = k*x + b(其中k为正数)。
    :param line_eq: 直线方程
    :param x_symbol: x变量的表现形式
    :param y_symbol: y变量的表现形式
    :return:
    >>> line_slope_inter_eq(BaseEq(['3*x + 5*y', 'a*x + 5'])).sympify()
    (y, x*(a/5 - 3/5) + 1)
    >>> line_slope_inter_eq(BaseEq(['3*x + b*y', 'a*x + 5*b'])).sympify()
    (y, x*(a/b - 3/b) + 5)
    """
    line_eq = to_base(line_eq, BaseEq)
    left, right = line_eq.sympify()
    left_fraction = fraction(left)
    right_fraction = fraction(right)
    f = expand(sympify(left * left_fraction[1] * right_fraction[1])) - expand(
        sympify(right * left_fraction[1] * right_fraction[1]))
    x_coeff, y_coeff = f.coeff(x_symbol), f.coeff(y_symbol)
    except_y_left = '0'  # old: except_y_left = ''
    for m, c in f.as_coefficients_dict().items():
        if str(m).find(y_symbol) < 0:  # old: str(m) != y_symbol
            except_y_left += '+' + str(c) + '*' + str(m)
    except_y_left = sympify(except_y_left)
    if x_coeff == 0:
        new_right = (-except_y_left) / y_coeff
        return BaseEq([y_symbol, new_right])
    elif y_coeff == 0:
        if except_y_left.coeff(x_symbol) < 0:
            except_y_left = - except_y_left
        return BaseEq(['0', except_y_left])
    # return BaseEq([y_symbol, collect((-except_y_left) / y_coeff, x_symbol)])
    right_e = expand(collect((-except_y_left) / y_coeff, x_symbol))
    right_e = collect(right_e, x_symbol)
    return BaseEq([y_symbol, right_e])


def line_point_slope_eq(line_eq, x_symbol='x', y_symbol='y', k_symbol='z'):
    """
    直线的点斜式方程。形如 y + a = k(x + b)
    :param line_eq: 任意直线方程
    :param x_symbol: 变量x的表示形式
    :param y_symbol: 变量y的表示形式
    :param k_symbol: 除x, y之外的第三个未知参数(这个是必传参数, 不然点斜式方程可以有无数个,无法确定)
    :return: 直线的点斜式
    >>> line_point_slope_eq(BaseEq(['s*(2*y - 3)', '(3*x + 1)']), k_symbol='s').sympify()
    (y - 3/2, 3*(x + 1/3)/(2*s))
    """
    line_eq = to_base(line_eq, BaseEq)
    left, right = line_eq.sympify()
    left, right = cancel(left), cancel(right)
    ln, ld = fraction(left)
    rn, rd = fraction(right)
    left, right = ln * rd, rn * ld
    f = expand(left - right)
    y_sym = expr_symbol(f, y_symbol)
    y_coeff = f.coeff(y_sym)
    f = expand(f / y_coeff)
    z_terms = sympify('0')
    for m, c in f.as_coefficients_dict().items():
        if str(m).find(k_symbol) >= 0 or str(m).find(x_symbol) >= 0:
            z_terms += m * c
    new_left = cancel(simplify(f - z_terms))
    new_right = -z_terms
    new_right = factor(new_right)
    new_right_n, new_right_d = fraction(new_right)
    new_right_n = collect(expand(new_right_n), x_symbol)
    x_coeff = new_right_n.coeff(x_symbol)
    right_z_terms = sympify('0')
    for m, c in new_right_n.as_coefficients_dict().items():
        if str(m).find(x_symbol) < 0:
            right_z_terms += m * c
    new_right_n = expr_symbol(new_right_n, x_symbol) + right_z_terms / x_coeff
    new_right = new_right_n * (x_coeff / new_right_d)
    return BaseEq([new_left, new_right])


def slope_vertex_point(line_eq, x_symbol='x', y_symbol='y', k_symbol='k'):
    """
    斜率包含参数的直线点斜式的顶点。
    :param line_eq: 任意直线的方程
    :param x_symbol:
    :param y_symbol:
    :param k_symbol:
    :return:
    >>> slope_vertex_point(BaseEq(['s*(2*y - 3)', '(3*x + 1)']), k_symbol='s')
    (-1/3, 3/2)
    >>> slope_vertex_point(BaseEq(['(2*y - 5)/(x+3)', 's']), k_symbol='s')
    (-3, 5/2)
    """
    slope_line_eq = line_point_slope_eq(line_eq, x_symbol=x_symbol,
                                        y_symbol=y_symbol, k_symbol=k_symbol)
    right_e, left_e = slope_line_eq.sympify()
    y_point = -sympify(str(right_e) + '-' + y_symbol)
    x_point = -sympify(str(left_e.coeff(expand(left_e).coeff(x_symbol))) + '-' + x_symbol)
    return x_point, y_point


def line_slope(line_eq, x_symbol='x', y_symbol='y'):
    """
    求直线的斜率
    :param line_eq: 直线方程
    :param x_symbol:
    :param y_symbol:
    :return: 直线的斜率, 如果k = 0则返回0, 如果y不存在,则返回None
    >>> line_slope(BaseEq([' -3*y + 5', '10-6*x']))
    2
    >>> line_slope(BaseEq([' -3*x + 5', '10-6*x']))

    >>> line_slope(BaseEq([' -3*y + 5', '10-6*y']))
    0
    >>> line_slope(['y', '0'])
    0
    """
    # 化简成形如y = k*x + b
    line_eq = line_slope_inter_eq(line_eq, x_symbol, y_symbol)
    y_left, right_expr = line_eq.sympify()
    if right_expr.coeff(x_symbol) == 0:
        return 0
    if y_left == 0:
        return None
    return right_expr.coeff(x_symbol)


def two_point_line_slope(p1, p2):
    """
    求两点之间的直线的斜率,如果两个点的x坐标相等则返回正无穷
    :param p1:
    :param p2:
    :return:
    >>> two_point_line_slope((4, 7), (3, 5))
    2
    >>> two_point_line_slope((3, 7), (3, 5))
    oo
    """
    if (sympify(p1[0]) - sympify(p2[0])) == 0:
        return S.Infinity
    return (sympify(p1[1]) - sympify(p2[1])) / (sympify(p1[0]) - sympify(p2[0]))


_ineq_op_symbols = ['>=', '>', '<=', '<']  # 不等式符号


def simplify_ineq(e, sym):
    """
    化简一元一次不等式, 形如 a*x <(>) b
    :param e: 表达式
    :param sym: 未知变量
    :return: 化简后的形式
    >>> simplify_ineq('a*x + b >= 2*x', 'x')
    (x*(a - 2), '>=', -b)
    """
    ineq_op_s = list(filter(lambda ineq_op_symbol: e.find(ineq_op_symbol) > 0, _ineq_op_symbols))[0]
    left_expr, right_expr = e.split(ineq_op_s)
    e = sympify(left_expr) - sympify(right_expr)
    sym_terms, others = expr_symb_terms_and_others(e, sym)
    return sym_terms, ineq_op_s, -others


def top_point_expr(e, x_symbol='x'):
    """
    一元二次方程的顶点式.顶点式：y=a(x-h)²+k,抛物线的顶点P（h,k）
    对于一般二次函数 y=ax^2+bx+c 其顶点坐标为 (-b/2a,(4ac-b²)/4a)
    :param e: 任何一元二次不等式方程
    :param x_symbol: 未知数
    :return: 一元二次方程的顶点式 和 顶点P
    >>> top_point_expr('5*x**2 + 6*x + c')
    (c + 5*(x + 3/5)**2 - 9/5, (-3/5, c - 9/5))
    """
    x_symbol = str(x_symbol)
    f = expand(sympify(e))
    a = f.coeff('%s**2' % x_symbol)
    b = f.coeff(x_symbol)
    x_sym = expr_symbol(f, x_symbol)
    c = f - (a * x_sym ** 2 + b * x_sym)
    h = -b / (2 * a)
    k = (4 * a * c - b ** 2) / (4 * a)
    return a * (x_sym - h) ** 2 + k, (h, k)


def satisfied_ineqs(ineqs, symbol_values, criticaled=False):
    """
    判断坐标点是否满足不等式组
    :param ineqs: 不等式组
    :param symbol_values: 坐标值
    :param criticaled: 是否取临界值
    :return:
    >>> satisfied_ineqs([['2*x + y', '>', '0'], ['x - 3*y', '<', '5'], ['x', '>', '0']], (('x', 4), ('y', 5)))
    True
    >>> satisfied_ineqs([['2*x + y', '>', '0'], ['x - 3*y', '<', '5'], ['x', '>', '0']], (('x', -5), ('y', -3)))
    False
    """
    ineqs = to_base_list(ineqs)
    intls = []
    for ineq in ineqs:
        left, op, right = ineq
        intl = solveset(''.join(
            [str(sympify(left).subs(symbol_values)), op, str(sympify(right).subs(symbol_values))]),
            domain=S.Reals)
        if intl == EmptySet() and criticaled:
            if str(sympify(left).subs(symbol_values)) == str(sympify(right).subs(symbol_values)):
                intl = FiniteSet(0)
        intls.append(intl)
    last_intl = reduce(lambda intv1, intv2: intv1.intersect(intv2), intls)
    return True if last_intl else False


def str_letters(s):
    """
    字符串中包含的字母
    :param s: 字符串
    :return: 包含的字母列表
    """
    p = '[a-zA-Z]'
    return sorted(list(set(re.findall(p, s))))


def ineqs_2_eqs(ineqs):
    """
    将不等式方程组转变成等式方程组
    :param ineqs:
    :return: 等式方程组
    >>> ineqs_2_eqs(BaseIneqs([['3*x + 2', '>', '5'], ['5*y + 3', '<', '0']])).sympify()
    [[3*x + 2, 5], [5*y + 3, 0]]
    """
    ineqs = to_base_list(ineqs)
    return BaseEqs([[ineq[0], ineq[2]] for ineq in ineqs])


def two_lines_have_intersections(eqs, x_symbol='x', y_symbol='y'):
    """
    判断两条直线是否有交点
    :param eqs:
    :param x_symbol: 表示x的符号
    :param y_symbol: 表示y的符号
    :return:
    >>> two_lines_have_intersections(BaseEqs([['x', '5'], ['x', '5']]))
    False
    >>> two_lines_have_intersections(BaseEqs([['y', '5'], ['x', '5']]))
    True
    """
    two_lines = to_base_list(eqs)
    return not line_slope(two_lines[0], x_symbol, y_symbol) == line_slope(two_lines[1], x_symbol, y_symbol)


def lines_intersections(eqs, x_symbol='x', y_symbol='y'):
    """
    求直线的所有交点
    :param eqs: 直线方程组
    :param x_symbol
    :param y_symbol
    :return: 所有直线的两辆交点
    >>> lines_intersections([['x', '3'], ['y', '5'], ['x', 'y']])
    [((['x', '3'], ['y', '5']), [(x, 3), (y, 5)]), ((['x', '3'], ['x', 'y']), [(x, 3), (y, 3)]), ((['y', '5'],
    ['x', 'y']), [(x, 5), (y, 5)])]
    >>> lines_intersections([['x', '3'], ['y', '5']])
    [((['x', '3'], ['y', '5']), [(x, 3), (y, 5)])]
    """
    eqs = to_base_list(eqs)
    two_line_eqs_list = list(combinations(eqs, 2))
    two_line_eqs_list = list(filter(lambda t_l: two_lines_have_intersections(t_l, x_symbol, y_symbol), two_line_eqs_list))
    if not two_line_eqs_list:
        return None
    return list(map(lambda two_line_eqs: (two_line_eqs, eqs_root(two_line_eqs, symbs=[x_symbol, y_symbol])), two_line_eqs_list))


def two_line_intersections(line1, line2, x_symbol='x', y_symbol='y'):
    """
    求两条直线的交点
    :param line1: 
    :param line2: 
    :param x_symbol: 
    :param y_symbol: 
    :return: 
    >>> two_line_intersections(['2*x + 3*y', '6'], ['y + x', '5'])
    [(x, 9), (y, -4)]
    """
    _eqs = [line1, line2]
    inters = lines_intersections(BaseEqs(_eqs), x_symbol, y_symbol)
    return inters[0][1]


def ineqs_intersections(ineqs, x_symbol='x', y_symbol='y'):
    """
    求不等式组的交点。跟 lines_intersections 用法一样。
    :param ineqs:
    :param x_symbol:
    :param y_symbol:
    :return:
    >>> ineqs_intersections(BaseIneqs([['x', '>=', '3'], ['y', '>=', '4'], ['x', '<=', '5']]))
    [(((x, '>=', 3), (y, '>=', 4)), [(x, 3), (y, 4)]), (((y, '>=', 4), (x, '<=', 5)), [(x, 5), (y, 4)])]
    """
    ineqs = to_base_list(ineqs)
    ineqs_map_lines = list(map(lambda two_ineqs: (two_ineqs, ineqs_2_eqs(two_ineqs)), combinations(ineqs, 2)))
    # 不等式组对应的交点
    ineqs_map_intervs = list(map(lambda two_ineq_and_eq: (two_ineq_and_eq[0], lines_intersections(two_ineq_and_eq[1], x_symbol, y_symbol)), ineqs_map_lines))
    ineqs_map_intervs = list(filter(lambda ineq_and_interv: ineq_and_interv[1], ineqs_map_intervs))  # 过滤掉那些没有交点的不等式组。
    return list(map(lambda ineqs_and_interv: (ineqs_and_interv[0], ineqs_and_interv[1][0][1]), ineqs_map_intervs))


def satisfied_ineqs_intersections(ineqs, x_symbol='x', y_symbol='y', criticaled=True):
    """
    多个约束条件两两组合求可行域的交点
    :param ineqs: 不等式组
    :param x_symbol:
    :param y_symbol:
    :param criticaled: 是否包含临界条件
    :return: 满足可行域的交点
    >>> satisfied_ineqs_intersections(BaseIneqs([['x', '>=', '2'], ['y', '>=', '1'], ['y', '<=', 'x']]))
    [(((x, '>=', 2), (y, '>=', 1)), [(x, 2), (y, 1)]), (((x, '>=', 2), (y, '<=', x)), [(x, 2), (y, 2)])]
    """
    return list(filter(lambda t: satisfied_ineqs(ineqs, t[1], criticaled=criticaled), ineqs_intersections(ineqs, x_symbol=x_symbol, y_symbol=y_symbol)))  # 满足不等式条件的交点


def perpendicular_line_and_foot(line_eq, point_value, y_symbol='y', x_symbol='x'):
    """
    求一个点到直线的垂线,返回垂线表达式和垂足
    :param line_eq:
    :param point_value:
    :param y_symbol:
    :param x_symbol:
    :return:
    """
    # 首先先化简成形如 y = k*x + b 标准直线表达式
    left, right = line_eq.sympify()
    f = sympify(left) - sympify(right)
    x_coeff, y_coeff = f.coeff(x_symbol), f.coeff(y_symbol)
    if not y_coeff:  # 如果为直线 x = k 那么垂足为
        const = f.as_independent('x')[0]
        return BaseEq(['y', point_value[1]]), (-const, point_value[1])
    except_y_left = '0'
    for m, c in f.as_coefficients_dict().items():
        if str(m) != y_symbol:
            except_y_left += '+' + str(c) + '*' + str(m)
    except_y_left = sympify(except_y_left)
    if x_coeff == 0:  # 如果x的系数为零, 那line_eq是一个一个平行于X轴的直线, 则其垂线为平行于Y轴的直线
        new_right = (-except_y_left) / y_coeff
        per_line_eq = BaseEq([x_symbol, point_value[0]])
        return per_line_eq, (point_value[0], new_right)
    elif y_coeff == 0:  # 如果y的系数为零,那么line_eq是一个平行于Y轴的直线, 则其垂线为平行于X轴的直线
        if except_y_left.coeff(x_symbol) < 0:
            except_y_left = - except_y_left
        constant = except_y_left.as_coefficients_dict[1]
        per_line_eq = BaseEq([y_symbol, point_value[1]])
        return per_line_eq, (-constant, point_value[1])
    new_right = (-except_y_left) / y_coeff
    # 此时化为形如y = k*x + b的形式了。
    line_eq = BaseEq([y_symbol, new_right])
    univariate_expr = line_eq.sympify()[1]
    k = univariate_expr.coeff(x_symbol)
    per_k = -1 / k
    new_eq = BaseEq(
        [y_symbol, expr_subs('k*(x-m) + n', (('k', per_k), ('m', point_value[0]), ('n', point_value[1])))])
    per_point = eqs_root(
        BaseEqs([[y_symbol, univariate_expr], [y_symbol, new_eq.sympify()[1]]]))
    return new_eq, tuple(map(lambda sv: sv[1], per_point))


# 求圆的圆心坐标和半径
def circle_center_and_radius(c_e, x_symbole='x', y_symbol='y'):
    """
    求圆的圆心坐标和半径
    :param c_e:
    :param x_symbole:
    :param y_symbol:
    :return:
    >>> circle_center_and_radius(['(x-3)**2 + (y - 5)**2', 9])
    ((3, 5), 3)
    >>> circle_center_and_radius(['x**2 + y**2 + 5*y + 2*x', '9'])
    ((-1, -5/2), sqrt(65)/2)
    """
    left, right = c_e
    #  形如 a*x**2 + b*y**2 + c*x + d*y + e = 0 其中 a = b
    common_e = expand(sympify(left) - sympify(right))
    a = common_e.coeff('%s**2' % x_symbole)
    c = common_e.coeff(x_symbole)
    d = common_e.coeff(y_symbol)
    e = common_e.as_coefficients_dict()[1]
    circle_point = (-c / 2 * a, -d / 2 * a)
    r = sqrt(c ** 2 + d ** 2 - 4 * a * e) / abs(2 * a)
    return circle_point, r


def to_ineq_expr_list(ineq_expr):
    """
    用不等式结构列表来表示不等式
    :param ineq_expr:
    :return:
    >>> to_ineq_expr_list('-5<=x<=0')
    ['-5', '<=', 'x', '<=', '0']
    >>> to_ineq_expr_list('a + b<=x<= a - b')
    ['a + b', '<=', 'x', '<=', ' a - b']
    """
    ineq_expr_list = []  # 用列表结构,表示不等式[1, <, a, <, 2, <, b, <, 3]。
    tmp_ineq_e = ineq_expr
    ineq_ops = ['>', '<', '>=', '<=']
    while tmp_ineq_e:
        ineq_op_index = list(filter(lambda i: i > 0, map(lambda ineq_op: tmp_ineq_e.find(ineq_op), ineq_ops)))
        if len(ineq_op_index) == 0:
            ineq_expr_list.append(tmp_ineq_e)
            break
        ineq_op_index = ineq_op_index[0]
        ineq_expr_list.append(tmp_ineq_e[0: ineq_op_index])
        ineq_op_step = 1 if tmp_ineq_e[ineq_op_index + 1] == '=' else 0
        ineq_expr_list.append(tmp_ineq_e[ineq_op_index: ineq_op_index + ineq_op_step + 1])
        tmp_ineq_e = tmp_ineq_e[ineq_op_index + ineq_op_step + 1:]
    return ineq_expr_list


def stand_ineq_expr_list(ineq_e):
    """
    标准化不等式。 化成形如 a <|> R
    :param ineq_e:
    :return:
    >>> stand_ineq_expr_list(['5', '>=', 'a'])
    ['a', '<=', '5']
    >>> stand_ineq_expr_list(['5', '<=', 'a'])
    ['a', '>=', '5']
    """
    if isinstance(ineq_e, str):
        ineq_e = to_ineq_expr_list(ineq_e)
    if len(ineq_e) == 3:
        if ineq_e[1].find('>') >= 0:  # a > 5, 5>a
            if sympify(ineq_e[0]).is_real:  # 5>a
                return [ineq_e[2], ineq_e[1].replace('>', '<'), ineq_e[0]]
            else:
                return ineq_e[::]
        else:  # a<5, 5<a
            if sympify(ineq_e[0]).is_real:  # 5<a
                return [ineq_e[2], ineq_e[1].replace('<', '>'), ineq_e[0]]
            else:
                return ineq_e[::]
    else:
        return ineq_e


def ineq_expr_list_2_interval(ineq_expr_list):
    """
    将不等式结构列表转变成Interval结构
    :param ineq_expr_list:
    :return:
    >>> ineq_expr_list_2_interval(['5', '<=', 'a', '<=', '10'])
    [5, 10]
    >>> ineq_expr_list_2_interval(['a', '>', '5'])
    (5, oo)
    """
    if len(ineq_expr_list) == 3:  # 一个端点
        if ineq_expr_list[1].find('>') >= 0:
            if sympify(ineq_expr_list[-1]).is_real:  # a>10
                return Interval(sympify(ineq_expr_list[2]), S.Infinity, left_open=ineq_expr_list[1].find('=') < 0)
            else:  # 10>a
                return Interval(-S.Infinity, sympify(ineq_expr_list[0]), right_open=ineq_expr_list[1].find('=') < 0)
        else:  # 10<a or a<10
            if sympify(ineq_expr_list[-1]).is_real:  # a<10
                return Interval(-S.Infinity, sympify(ineq_expr_list[-1]), right_open=ineq_expr_list[1].find('=') < 0)
            else:  # 10<a
                return Interval(sympify(ineq_expr_list[0]), S.Infinity, left_open=ineq_expr_list[1].find('=') < 0)
    else:  # 两个端点
        if ineq_expr_list[1].find('<') >= 0:  # 3<a<5
            return Interval(sympify(ineq_expr_list[0]), sympify(ineq_expr_list[-1]),
                            left_open=ineq_expr_list[1].find('=') < 0,
                            right_open=ineq_expr_list[-2].find('=') < 0)
        else:  # 5>a>3
            return Interval(sympify(ineq_expr_list[-1]), sympify(ineq_expr_list[0]),
                            left_open=ineq_expr_list[-2].find('=') < 0,
                            right_open=ineq_expr_list[1].find('=') < 0)


def analy_hide_ineq(ineq_str):
    """
    根据变量之间的不等式表达式,分析隐含的不等式关系
    :param ineq_str:
    :return:
    >>> analy_hide_ineq('1>a>=b>=6')
    (a - b, '>=', 0)
    (-a + b, '<=', 0)
    (a/b, '>=', 1)
    (b/a, '<=', 1)
    """
    if ineq_str.find('pi') >= 0:
        ineq_str = ineq_str.replace('pi', 'π')
    p = r'([A-Za-z])(.*)(<=|<|>=|>)([A-Za-z])'
    # p = r'([A-Za-z])(<=|<|>=|>).*(<=|<|>=|>)([A-Za-z])'
    m = re.search(p, ineq_str)
    if not m:
        return None
    left_symb, left_op, right_op, right_symb = m.groups()
    include_eq = left_op.find('=') >= 0 and right_op.find('=') >= 0
    ineq_op = right_op if not left_op or include_eq else right_op.replace('=', '')
    rever_ineq_op = ineq_op.replace('>', '<') if ineq_op.find('>') >= 0 else ineq_op.replace('<', '>')
    left_symb, right_symb = symbols(left_symb + ' ' + right_symb)
    ineq1 = BaseIneq([left_symb - right_symb, ineq_op, 0])
    ineq2 = BaseIneq([right_symb - left_symb, rever_ineq_op, 0])
    ineq3 = BaseIneq([left_symb / right_symb, ineq_op, 1])
    ineq4 = BaseIneq([right_symb / left_symb, rever_ineq_op, 1])
    return ineq1, ineq2, ineq3, ineq4


def solve_param_absed_ineq(left_hand, ineq_op, right_hand, x_symbol='x'):
    """
    解决含参数的绝对值不等式 |f(x)| > g(a)
    :param left_hand: 左式(绝对值项)
    :param ineq_op: 不等式符号
    :param right_hand: 右式(不含绝对值项)
    :param x_symbol: x的表现符号
    :return: 如果未知变量的系数含有参数的话,则返回分类讨论的情况,否则直接返回区间
    >>> solve_param_absed_ineq(sympify('Abs(3*x + a)'), '<', '5')
    (-a/3 - 5/3, -a/3 + 5/3)
    >>> solve_param_absed_ineq(sympify('Abs(-3*x + a)'), '>=', '5')
    (-oo, a/3 - 5/3] U [a/3 + 5/3, oo)
    >>> solve_param_absed_ineq(sympify('Abs(-a*x + b)'), '>=', '5')
    [('a >0', (0, oo), (-oo, (b - 5)/a] U [(b + 5)/a, oo)), ('a <0', (-oo, 0), (-oo, (b + 5)/a] U [(b - 5)/a, oo))]
    """
    left_hand = sympify(left_hand)
    right_hand = sympify(right_hand)
    x_coeff = left_hand.args[0].coeff(x_symbol)  # x的系数
    first_ineq = simplify_ineq(str(left_hand.args[0]) + ineq_op + str(right_hand), x_symbol)
    second_ineq = simplify_ineq(str(left_hand.args[0]) + ineq_op.replace('>', '<') + str(-right_hand), x_symbol)
    positive_value = sympify(first_ineq[-1]) / sympify(first_ineq[0]).coeff(x_symbol)
    negative_value = sympify(second_ineq[-1]) / sympify(second_ineq[0]).coeff(x_symbol)
    opened = ineq_op.find('=') < 0
    if x_coeff.is_real:  # 如果x的系数为常数
        if x_coeff > 0:
            if ineq_op.find('>') >= 0:  # |f(x)| > g(a)
                return Interval(positive_value, S.Infinity, left_open=ineq_op.find('=') < 0).union(
                    Interval(-S.Infinity, negative_value, right_open=ineq_op.find('=') < 0))
            else:
                return Interval(negative_value, positive_value, left_open=opened, right_open=opened)
        else:
            if ineq_op.find('>') >= 0:
                return Interval(negative_value, S.Infinity, left_open=opened).union(
                    Interval(-S.Infinity, positive_value, right_open=opened))
            else:
                return Interval(positive_value, negative_value, left_open=opened, right_open=opened)
    else:  # 如果含参 a*x; a代表一个整体
        intervals = []
        # a>0的时候
        if ineq_op.find('>') >= 0:
            intervals.append((str(x_coeff) + ' >0', solveset(str(x_coeff) + ' >0', domain=S.Reals),
                              Interval(positive_value, S.Infinity, left_open=ineq_op.find('=') < 0).union(
                                  Interval(-S.Infinity, negative_value, right_open=ineq_op.find('=') < 0))))
            intervals.append((str(x_coeff) + ' <0', solveset(str(x_coeff) + ' <0', domain=S.Reals),
                              Interval(negative_value, S.Infinity, left_open=opened).union(
                                  Interval(-S.Infinity, positive_value, right_open=opened))))
        else:
            intervals.append((str(x_coeff) + ' >0', solveset(str(x_coeff) + ' >0', domain=S.Reals),
                              Interval(negative_value, positive_value, left_open=opened, right_open=opened)))
            intervals.append((str(x_coeff) + ' <0', solveset(str(x_coeff) + ' <0', domain=S.Reals),
                              Interval(positive_value, negative_value, left_open=opened, right_open=opened)))

        return intervals


def print_interval(intv, x_symbol='x'):
    """
    打印区间
    :param intv:
    :param x_symbol:
    :return:
    >>> print_interval(Interval(-5, 3, left_open=True))
    '-5<x<=3'
    """
    if isinstance(intv, FiniteSet):  # 如果是等式
        vs = list(intv)
        print_text = ''
        for v in vs:
            print_text += '%s=%s ' % (new_latex(sympify(x_symbol)), new_latex(v))
        return print_text
    x_symbol = str(x_symbol)
    left_value = intv.left
    left_open = intv.left_open
    right_value = intv.right
    right_open = intv.right_open
    left_expr = str(left_value) + ('<' if left_open else '<=')
    right_expr = ('<' if right_open else '<=') + str(right_value)
    if left_value == -S.Infinity:
        return x_symbol + right_expr
    elif right_value == S.Infinity:
        return left_expr + x_symbol
    else:
        return left_expr + x_symbol + right_expr


def interval_add_const(intv, const):
    """
    区间加上一个常数
    :param intv: 区间
    :param const: 参数
    :return:
    >>> interval_add_const(Interval(-5, 10), 20)
    [15, 30]
    """
    return Interval(intv.left + const, intv.right + const, left_open=intv.left_open, right_open=intv.right_open)


def intervals_union(intervals):
    """
    多个区间的并集
    :param intervals:
    :return:
    >>> intervals_union([Interval(1, 2), Interval(3, 5), Interval(-1, 5)])
    [-1, 5]
    >>> intervals_union([Interval(-1, 2), Interval(3, 5), Interval(5, 10)])
    [-1, 2] U [3, 10]
    """
    return reduce(lambda intv1, intv2: intv1.union(intv2), intervals)


def intervals_intersect(intervals):
    """
    多个区间的交集
    :param intervals: 多个区间列表
    :return:
    >>> intervals_intersect([Interval(-3, 5), Interval(-1, 3), Interval(-4, 2)])
    [-1, 2]
    """
    return reduce(lambda intv1, intv2: intv1.intersect(intv2), intervals)


def reciprocal_interval(s_interval):
    """
    求一个区间的倒数范围
    :param s_interval: 取值范围
    :return: 范围求倒后的范围(一个Interval结构或者Union结构)
    >>> reciprocal_interval(Interval(-1, 3))
    (-oo, -1] U [1/3, oo)
    """

    def _split_interval(intv):
        if intv.left * intv.right < 0:
            return Interval(intv.left, 0, left_open=intv.left_open, right_open=True).union(
                Interval(0, intv.right, left_open=True, right_open=intv.right_open))
        else:
            return intv

    def _simple_recipocal_scope(intv):
        from sympy.core.numbers import Infinity, NegativeInfinity, NaN
        if intv.left * intv.right == 0 or isinstance(intv.left * intv.right, NaN):
            if intv.left == 0:
                return Interval(1 / intv.right, S.Infinity, left_open=intv.right_open, right_open=True)
            elif intv.right == 0:
                return Interval(-S.Infinity, 1 / intv.left, left_open=True, right_open=intv.left_open)
        elif isinstance(intv.left * intv.right, Infinity) or isinstance(intv.left * intv.right, NegativeInfinity):
            if intv.left == -S.Infinity:
                return Interval(1 / intv.right, 0, left_open=intv.right_open, right_open=True)
            elif intv.right == S.Infinity:
                return Interval(0, 1 / intv.left, left_open=True, right_open=intv.left_open)
        else:
            return Interval(1 / intv.right, 1 / intv.left, left_open=intv.right_open, right_open=intv.left_open)

    if not s_interval.left or not s_interval.right:
        return s_interval
    if s_interval.left * s_interval.right < 0:
        intervals = _split_interval(s_interval).args
        return _simple_recipocal_scope(intervals[0]).union(_simple_recipocal_scope(intervals[1]))
    else:
        return _simple_recipocal_scope(s_interval)


def two_interval_add(a_interval, b_interval):
    """
    两区间相加
    :param a_interval:
    :param b_interval:
    :return: 区间相加后的区间
    >>> two_interval_add(Interval(-3, 3), Interval(4, 5))
    [1, 8]
    """
    min_value = a_interval.left + b_interval.left
    min_open = a_interval.left_open or b_interval.left_open
    max_value = a_interval.right + b_interval.right
    max_open = a_interval.right_open or b_interval.right_open
    return Interval(min_value, max_value, left_open=min_open, right_open=max_open)


def interval_add(intv, other):
    """
    区间做加法
    :param intv: 区间
    :param other: 常数或者其他区间
    :return:
    >>> interval_add(Interval(0, 3), -3)
    [-3, 0]
    >>> interval_add(Interval(0, 3), Interval(5, 10))
    [5, 13]
    """
    if isinstance(other, Interval):
        return two_interval_add(intv, other)
    elif sympify(other).is_real:
        return interval_add_const(intv, sympify(other))
    else:
        raise Exception('Interval can only add Interval or Real')


def two_interval_mul(a_interval, b_interval):
    """
    两区间相乘
    :param a_interval:
    :param b_interval:
    :return:
    >>> two_interval_mul(Interval(-5, -1), Interval(-1, 5))
    [-25, 5]
    """

    def real_mul(r1, r2):
        r = r1 * r2
        if isinstance(r, NaN):
            r = 0
        return r

    a_range = [(a_interval.left, a_interval.left_open), (a_interval.right, a_interval.right_open)]
    b_range = [(b_interval.left, b_interval.left_open), (b_interval.right, b_interval.right_open)]
    ab_range = [(a_i, b_i) for a_i in a_range for b_i in b_range]
    # ab_range_value = map(lambda ab: (ab[0][0] * ab[1][0], ab[0][1] and ab[1][1]), ab_range)
    ab_range_value = map(lambda ab: (real_mul(ab[0][0], ab[1][0]), ab[0][1] and ab[1][1]), ab_range)
    max_value = max(ab_range_value, key=lambda ab: ab[0])
    min_value = min(ab_range_value, key=lambda ab: ab[0])
    return Interval(min_value[0], max_value[0], left_open=min_value[1], right_open=max_value[1])


def interval_mul_rel(s_interval, rel):
    """
    区间与实数相乘
    :param s_interval:
    :param rel:
    :return:
    >>> interval_mul_rel(Interval(-5, -3, left_open=True), sympify('1/3'))
    (-5/3, -1]
    >>> interval_mul_rel(Interval(-5, -3, left_open=True), sympify('-1/3'))
    [1, 5/3)
    """
    rel = sympify(rel)
    if rel > 0:
        return Interval(s_interval.left * rel, s_interval.right * rel, left_open=s_interval.left_open,
                        right_open=s_interval.right_open)
    else:
        return Interval(s_interval.right * rel, s_interval.left * rel, left_open=s_interval.right_open,
                        right_open=s_interval.left_open)


def interval_mul(intl, other):
    """
    区间乘操作
    :param intl: 区间
    :param other: 区间或者常数
    :return:
    >>> interval_mul(Interval(3, 5), Interval(-2, 5))
    [-10, 25]
    """
    if isinstance(intl, Interval) and isinstance(other, Interval):
        return two_interval_mul(intl, other)
    elif isinstance(intl, Interval) and sympify(other).is_real:
        return interval_mul_rel(intl, other)
    elif isinstance(other, Interval) and sympify(intl).is_real:
        return interval_mul_rel(other, intl)
    else:
        raise Exception('Interval can only mul Interval or const')


def interval_abs(s_interval):
    """
    求区间的绝对值范围
    :param s_interval:
    :return:
    >>> interval_abs(Interval(-S.Infinity, -3))
    [3, oo)
    >>> interval_abs(Interval(-3, 3))
    (0, 3)
    """
    if abs(s_interval.left) == abs(s_interval.right):
        right_open = s_interval.left_open and s_interval.right_open
        return Interval(0, s_interval.right, left_open=False, right_open=right_open)
    s_interval = [(s_interval.left, s_interval.left_open), (s_interval.right, s_interval.right_open)]
    absed_interval = map(lambda s_i: (abs(s_i[0]), s_i[1]), s_interval)
    max_value = max(absed_interval, key=lambda si: si[0])
    if s_interval[0][0] * s_interval[1][0] < 0:
        return Interval(0, max_value[0], right_open=max_value[1])
    min_value = min(absed_interval, key=lambda si: si[0])
    if max_value[0] == min_value[0]:
        min_value = (sympify('0'), False)
    return Interval(min_value[0], max_value[0], left_open=min_value[1], right_open=max_value[1])


def complete_fraction(f):
    """
    完备的fraction, 有些情况 cancel 不会强制 比如形如 a/3 + 1/2 就不会
    :param f:
    :return:
    """
    f = sympify(f)
    if f != cancel(f):
        return fraction(cancel(f))
    mons = list(map(lambda _: _[0] * _[1], f.as_coefficients_dict().items()))
    if len(mons) == 1:
        return fraction(f)
    mos = list(map(lambda mon: fraction(mon)[0], mons))
    des = list(map(lambda mon: fraction(mon)[1], mons))
    mo_des = list(zip(mos, des))
    des_lcm = reduce(lcm, des)
    mos2 = list(map(lambda mo_de: mo_de[0] * des_lcm / mo_de[1], mo_des))
    mo = reduce(lambda a, b: a + b, mos2)
    return mo, des_lcm


def func_domain(f, symb_intls_dict):
    """
    求函数值域的问题。 如果是单变量则, 变成求最值问题, 如果是多变量则分别计算每个项的区间然后想加(目前只考虑多变量)
    最简单的方式 就是 k1*a + k2*b 的形式(其中a, b为变量) 也有可能为 |k1*a + k2*b| 形式
    :param f: 函数表达式
    :param symb_intls_dict: 每个变量的区间
    :return: 函数的值域
    >>> func_domain('(3*a + 6)/5', {'a': Interval(3, 5, left_open=True), 'b': Interval(2, 6), 'c': Interval(1, 3)})
    (3, 21/5]
    >>> func_domain('a + b', {'a': Interval(2, 5), 'b': Interval(3, 6)})
    [5, 11]
    >>> func_domain('Abs(3*a - 5*b)', {'a': Interval(2, 5), 'b': Interval(3, 6)})
    [0, 24]
    >>> func_domain('a/b', {'a': Interval(-pi / 2, pi / 2), 'b': Interval(-pi / 2, pi / 2)})
    (-oo, oo)
    """
    f = cancel(f)
    # mo, de = fraction(f)  # 分子,分母
    mo, de = complete_fraction(f)  # 分子,分母
    mo_intl = sympify('0')
    if not mo.is_real or isinstance(mo, Abs):
        # for m, c in mo.as_coefficients_dict().items(): # old
        for m, c in coefficients_dict(mo).items():
            if m.is_Symbol:
                tmp_intl = interval_mul_rel(symb_intls_dict[str(m)], c)
            elif m.is_real and not isinstance(m, Abs):  # 如果m是实数
                tmp_intl = m * c
            elif isinstance(m, Mul):
                tmp_intl = interval_mul_rel(reduce(
                    lambda t1, t2: interval_mul(func_domain(t1, symb_intls_dict), func_domain(t2, symb_intls_dict)),
                    m.args), c)
            elif isinstance(m, Pow):  # 如果是Pow类型，形如 f(x)**n。 2016-12-12 update
                m_args = m.args
                base_list = [m_args[0] for _ in range(m_args[1])]
                tmp_intl = interval_mul_rel(reduce(
                    lambda t1, t2: interval_mul(func_domain(t1, symb_intls_dict), func_domain(t2, symb_intls_dict)),
                    base_list), c)
            elif isinstance(m, Abs):
                tmp_intl = interval_mul(interval_abs(func_domain(list(m.args)[0], symb_intls_dict)), c)
            else:
                tmp_intl = func_domain(m * c, symb_intls_dict)
            if tmp_intl.is_real and mo_intl.is_real:
                mo_intl = mo_intl + tmp_intl
            elif tmp_intl.is_real and isinstance(mo_intl, Interval):
                mo_intl = interval_add(mo_intl, tmp_intl)
            else:
                mo_intl = interval_add(tmp_intl, mo_intl)
    else:
        mo_intl = mo
    if de.is_real and mo_intl.is_real:
        return mo_intl / de
    elif de.is_real and isinstance(mo_intl, Interval):
        de_interval = 1 / de
        return interval_mul(mo_intl, de_interval)
    else:
        de_interval = func_domain(de, symb_intls_dict)
        recip_interl = reciprocal_interval(de_interval)
        if isinstance(recip_interl, Union):
            return intervals_union(map(lambda intl: interval_mul(mo_intl, intl), recip_interl.args))
        return interval_mul(mo_intl, recip_interl)


def solve_ineqs(ineqs):
    """
    解不等式组
    :param ineqs: 不等式组
    :return:
    >>> solve_ineqs(['2*x + 1 >= 0', 'x - 4 <= 0', '3*x + 5 <= 7', 'x >= 1/3'])
    [1/3, 2/3]
    """
    return reduce(lambda i1, i2: i1.intersect(i2), map(lambda ineq: solveset(sympify(ineq), domain=S.Reals), ineqs))


def find_abs_items(tmp_f):
    """
    查找含有绝对值表达式中的绝对值中的表达式
    :param tmp_f: 
    :return: 
    >>> find_abs_items('Abs(2*x + 3*y) + a*x + b*y')
    [2*x + 3*y]
    """
    abs_reg = 'Abs\\(.*?\\)'
    tmp_f_str = str(tmp_f)
    tmp_abs_items = find_reg_expr(abs_reg, tmp_f_str)
    last_abs_items = []
    for abs_item in tmp_abs_items:
        abs_item_f = sympify(abs_item)
        last_abs_items.append(abs_item_f.args[0])
    return last_abs_items


def simply_absed_expr(absed_str, s_interval, x_symbol='x'):
    """
    化简绝对值表达式:去掉绝对值符号
    :param absed_str: 包含绝对值符号的表达式字符串
    :param s_interval: 变量的取值范围
    :param x_symbol: 变量的符号
    :return: 根据变量范围去掉绝对值符号的表达式
    >>> simply_absed_expr('Abs(2*x + 1) + Abs(x - 4)', Interval(-S.Infinity, sympify('-1/2')))
    -3*x + 3
    >>> simply_absed_expr('Abs(2*x + 1) + Abs(x - 4)', Interval(sympify('-1/2'), 4))
    x + 5
    """
    f = str(sympify(absed_str))
    # absed_items = re.findall('\|(.*?)\|', f) if str(f).find('|') >= 0 else re.findall('Abs\((.*?)\)', f)  # 提取绝对值项目
    absed_items = find_abs_items(f)
    for absed_item in absed_items:
        absed_item = str(absed_item)
        if s_interval.left != -S.Infinity:
            if expr_subs(absed_item, (x_symbol, s_interval.left)) >= 0:
                f = f.replace('|%s|' % absed_item, '(' + absed_item + ')')
                f = f.replace('Abs(%s)' % absed_item, '(' + absed_item + ')')
            else:
                f = f.replace('|%s|' % absed_item, '(' + str(-sympify(absed_item)) + ')')
                f = f.replace('Abs(%s)' % absed_item, '(' + str(-sympify(absed_item)) + ')')
        else:
            if expr_subs(absed_item, (x_symbol, s_interval.right)) <= 0:
                f = f.replace('|%s|' % absed_item, '(' + str(-sympify(absed_item)) + ')')
                f = f.replace('Abs(%s)' % absed_item, '(' + str(-sympify(absed_item)) + ')')
            else:
                f = f.replace('|%s|' % absed_item, '(' + absed_item + ')')
                f = f.replace('Abs(%s)' % absed_item, '(' + absed_item + ')')
    return expand(sympify(f))


def segment_absed_expr(absed_e):
    """
    将绝对值函数转化为分段函数
    :param absed_e: 含绝对值的函数
    :return: 分段后的函数列表
    >>> segment_absed_expr('Abs(2*x + 1)-Abs(x - 4)')
    [(-x - 5, (-oo, -1/2]), (3*x - 3, [-1/2, 4]), (x + 5, [4, oo))]
    """
    f = sympify(absed_e)
    # abs_items = re.findall('\|(.*?)\|', f)  # 提取绝对值项目
    # abs_items = re.findall('\|(.*?)\|', f) if str(f).find('|') >= 0 else re.findall('Abs\((.*?)\)', f)
    abs_items = find_abs_items(str(f))
    removed_abs_items = []
    # 先去掉那些绝对值里面恒成立的表达式的绝对值符号
    for temp_abs_item in abs_items:
        intl = solveset(str(temp_abs_item) + '>0', domain=S.Reals)
        if intl == S.Reals:
            f = f.subs('Abs(%s)' % temp_abs_item, temp_abs_item)
            removed_abs_items.append(temp_abs_item)
            # del abs_items[i]
        elif intl == EmptySet():
            f = f.subs('Abs(%s)' % temp_abs_item, -sympify(temp_abs_item))
            removed_abs_items.append(temp_abs_item)
            # del abs_items[i]
    for remove_abs_item in removed_abs_items:
        abs_items.remove(remove_abs_item)
    if not abs_items:
        return [(f, Interval(-S.Infinity, S.Infinity)), ]
    abs_item_ineqs = map(lambda abs_item: (str(abs_item) + '<=0', str(abs_item) + '>=0'), abs_items)
    abs_item_faltten_ineqs = flatten(abs_item_ineqs)
    combin_ineqs = combinations(abs_item_faltten_ineqs, len(abs_items))

    def dup_pair_ineqs(ineqs):
        return filter(lambda two_ineqs:
                      to_ineq_expr_list(two_ineqs[0])[0] ==
                      to_ineq_expr_list(two_ineqs[1])[0],
                      combinations(ineqs, 2))

    combin_ineqs = filter(lambda ineqs: len(dup_pair_ineqs(ineqs)) == 0, combin_ineqs)
    intervals = filter(lambda intv: not isinstance(intv, EmptySet), map(solve_ineqs, combin_ineqs))
    f_segments = map(lambda intv: (simply_absed_expr(f, intv), intv), intervals)
    return f_segments


def solve_expr(e):
    """
    解条件表达式
    :param e: 等式或不等式
    :return: 解集
    >>> solve_expr('3<0')
    EmptySet()
    >>> solve_expr('a**2 + 2*a + 1 >= 0')
    (-oo, oo)
    >>> solve_expr(2)
    [1/2, oo)
    >>> solve_expr('a**2 - 2*a + 1=0')
    {1}
    """
    try:
        ss = solveset(e, domain=S.Reals)
    except Exception:
        e = str(e)
        left, right = e.split('=')
        f = sympify(left.strip()) - sympify(right.strip())
        ss = solveset(str(f), domain=S.Reals)
    return ss


def split_mons_const(e, x_symbol=None):
    """
    获取变量的常数项
    :param e:
    :param x_symbol: 把那个当做变量
    :return:
    """
    symons, const = symoms_and_const(e, x_symbol=x_symbol)
    mons = reduce(lambda a, b: a + b, symons)
    return mons, const


def sqrtsimp(expr, sym_intvls):
    """
    化简含根号的表达式
    :param expr: 目标表达式
    :param sym_intvls: 未知变量的取值范围
    :return: 化简后的表达式
    >>> sqrtsimp(sqrt(sympify('a**2*b**2')), [('a', Interval(0, S.Infinity)), ('b', Interval(0, 5))])
    a*b
    """
    f = sympify(expr)
    pos_syms_list = [str(s) for s, i in sym_intvls if i > 0]
    pos_syms_str = ' '.join(pos_syms_list)
    pos_symbs = symbols(pos_syms_str, positive=True)
    if iterable(pos_symbs):
        for ps in pos_symbs:
            f = f.subs(str(ps), ps)
    else:
        f = f.subs(str(pos_symbs), pos_symbs)
    return sqrtdenest(f)


def eqsimp(eq):
    """
    输出友好的等式
    :param eq: 等式
    :return:
    >>> eqsimp(BaseEq(['x + y - 5', '6'])).sympify()
    (x + y, 11)
    """
    ll, r = eq.sympify()
    f = ll - r
    cont = [c for v, c in f.as_coefficients_dict().items() if v == 1][0]
    ll = f - cont
    r = 0 - cont
    return BaseEq([ll, r])


def linear_frac_simp(frac, x_symbol='x'):
    """
    分母为一阶的分式化简。 形如 (c*x**2 + d*x + e)/(a*x + b)
    :param frac: 分式表达式
    :param x_symbol: x符号表示
    :return:
    >>> linear_frac_simp('(5*x**2 + x + 1)/(x + 1)')
    5*x - 4 + 5/(x + 1)
    >>> linear_frac_simp('(6*x**2 + 3*x + 1)/(3*x + 1)')
    2*x + 1/3 + 2/(9*x + 3)
    """
    x_symbol = sympify(x_symbol)
    f = cancel(frac)
    symbs = f.free_symbols
    if len(symbs) == 1:
        x_symbol = list(symbs)[0]
    nn, de = fraction(f)
    if de == 1:
        return nn
    a = de.coeff(x_symbol)
    b = symoms_and_const(de)[1]
    c = nn.coeff(sympify(x_symbol) ** 2)
    d = nn.coeff(x_symbol)
    e = symoms_and_const(nn)[1]
    t1 = 0 if a == 0 else c / a * x_symbol
    t2 = 0 if a == 0 else (a * d - b * c) / a ** 2
    t3 = simplify((e * a ** 2 - a * b * d + b ** 2 * c) / (a ** 2 * de))
    t3_n, t3_d = fraction(t3)
    t3_d = expand(t3_d)
    t3 = t3_n / t3_d
    return t1 + t2 + t3


def quad_frac_simp(frac, x_symbol='x'):
    """
    分母为二次阶的分式的化简 形如(d*x**2 + e*x + f)/(a*x**2 + b*x + c)
    :param frac: 分式
    :param x_symbol: 未知变量
    :return: 只有分母含有二次阶的的多项式 形如 k + (h*x + w)/(a*x**2 + b*x + c)
    >>> quad_frac_simp('(2*x**2 + 4*x)/(x**2 + 2*x + 3)')
    2 - 6/(x**2 + 2*x + 3)
    >>> quad_frac_simp('(2*x**2 + 4*x + 6)/(x**2 + 2*x + 3)')
    2
    >>> quad_frac_simp('(3*x**2 + 5*x + 6)/(x**2 + 2*x + 3)')
    -(x + 3)/(x**2 + 2*x + 3) + 3
    >>> quad_frac_simp('(x + 3)/(x**2 + 5*x + 2)')
    (x + 3)/(x**2 + 5*x + 2)
    """
    x_symbol = sympify(x_symbol)
    f = cancel(sympify(frac))
    f = simplify(f)
    m, de = fraction(f)
    if de == 1:
        return f
    d = m.coeff(pow(x_symbol, 2))
    e = m.coeff(x_symbol)
    f = symoms_and_const(m)[1]
    a = de.coeff(pow(x_symbol, 2))
    b = de.coeff(x_symbol)
    c = symoms_and_const(de)[1]
    k = 0 if a == 0 else d / a
    h = e if a == 0 else (a * e - b * d) / a
    w = f if a == 0 else (a * f - c * d) / a
    return k + simplify((h * x_symbol + w) / de)


def degr_mons(e, x_symbol='x', reverse=False):
    """
    提取多项式中的每个单项,按阶次的大小排序
    :param e: 多项式
    :param x_symbol: 未知变量
    :param reverse: 是否翻转
    :return:
    """
    e = sympify(e)
    x_symbol = sympify(x_symbol)
    symbs = e.free_symbols
    if len(symbs) == 1 and sympify(x_symbol) == sympify('x'):  # 防止有些时候忘记写了
        x_symbol = list(symbs)[0]
    e = expand(sympify(e))
    mons = map(lambda t: t[0] * t[1], e.as_coefficients_dict().items())
    d_ms = map(lambda m: (m, degree(m, x_symbol)), mons)
    return sorted(d_ms, key=lambda d_m: d_m[1], reverse=reverse)


def frac_simp(frac, x_symbol='x'):
    """
    分式化简, 最终分子的阶次要比分母低(也可以成为分离常数) 跟cancel是可逆的。
    :param frac: 分式
    :param x_symbol: 未知变量
    :return:
    >>> frac_simp('(x**4 - x**2)/(x**2 + 1)')
    x**2 - 2 + 2/(x**2 + 1)
    >>> frac_simp('(2*x**4 + 2*x**2)/(x**2 + 1)')
    2*x**2
    >>> frac_simp('(2*x**4 + 2*x**2 + 5)/(x**2 + 1)')
    2*x**2 + 5/(x**2 + 1)
    >>> frac_simp('(3*x**4 + 5*x**3 + 3*x + 7)/(x**2 + 2)')
    3*x**2 + 5*x - (7*x - 19)/(x**2 + 2) - 6
    >>> frac_simp('(x**3 + 2*x**2 - 3*x - 5)/(x**2 + 3)')
    x - (6*x + 11)/(x**2 + 3) + 2
    >>> frac_simp('(x**2 + x + 1)/(2*x)')
    x/2 + 1/2 + 1/(2*x)
    >>> frac_simp('(a + 5*b*x**2 + 5*x)/(b*x + 1)')
    a/(b*x + 1) + 5*x
    """
    x_symbol = sympify(x_symbol)
    f = cancel(frac)
    symbs = f.free_symbols
    if len(symbs) == 1:
        x_symbol = list(symbs)[0]
    mo, de = fraction(f)  # 分子,分母
    if de.is_real:
        return f
    mo_mons_degrs = degr_mons(mo, reverse=True, x_symbol=x_symbol)  # 提取分子中每一项,按从大到小排序
    mo_max_degr = mo_mons_degrs[0][1]  # 分子的最大阶次
    de_mons_degrs = degr_mons(de, reverse=True, x_symbol=x_symbol)  # 提取分母中每一项,按从大到小排序
    de_max_degr = de_mons_degrs[0][1]  # 分母的最大阶次
    if mo_max_degr < de_max_degr:  # 如果分子的阶次小于分母的阶次则不用进行计算
        return f
    de_max_degr_mon_coeff = de_mons_degrs[0][0].coeff(pow(x_symbol, de_max_degr))  # 分母的最大阶次的项对应的系数
    tmp_mo = mo
    redu_mons = []  # 化简后的项
    for mo_mon, degr in mo_mons_degrs:
        if degr < de_max_degr:
            break
        tmp_mo = tmp_mo - mo_mon  # 先减去当前要化简的项
        mon_coeff = mo_mon.coeff(pow(x_symbol, degr))  # 当前的项的系数
        rat = mon_coeff / de_max_degr_mon_coeff  # 比率(当前项的系数除于分子阶次最大的项)
        fill_mons = [0] if not de_mons_degrs[1:] else map(lambda d: d[0] * rat, de_mons_degrs[1:])  # 补项
        fac_mon = pow(x_symbol, degr - de_max_degr)  # 分解后相乘的项
        fill_mons = map(lambda fm: fac_mon * fm, fill_mons)
        fill_f = reduce(lambda a, b: a + b, fill_mons)
        tmp_mo = simplify(tmp_mo - fill_f)  # 化简的时候加上了该项，所以原式子要减去该项
        redu_mon = rat * fac_mon
        redu_mons.append(redu_mon)
    rem_frac = cancel(tmp_mo / de)
    rem_mo, remo_de = fraction(rem_frac)
    if degr_mons(rem_mo, reverse=True, x_symbol=x_symbol)[0][1] >= \
            degr_mons(remo_de, reverse=True, x_symbol=x_symbol)[0][1]:
        rem_frac = frac_simp(rem_frac, x_symbol)
    sep_mons = reduce(lambda a, b: a + b, redu_mons)
    return sep_mons + rem_frac


def psa_simp(e, x_symbol='x'):
    """
    将含分式的表达式转换为积定和表达式 最后转换为形如 k*(c*x + d) + e/(c*x + d) + cont的形式
    :param e: 含有分式的表达式 形如 a*x + b + e/(c*x + d)
    :param x_symbol: 未知变量的表现形式
    :return: 形如 k*(c*x + d) + e/(c*x + d) + cont的形式
    >>> psa_simp('(6*x**2 + 3*x + 1)/(x + 1)')
    6*(x + 1) - 9 + 4/(x + 1)
    >>> psa_simp('(6*x**2 + 3*x + 1)/(3*x + 1)')
    2*(9*x + 3)/9 - 1/3 + 2/(9*x + 3)
    """
    x_symbol = sympify(x_symbol)
    f = linear_frac_simp(e)
    symbs = f.free_symbols
    if len(symbs) == 1:
        x_symbol = list(symbs)[0]
    terms, b = symoms_and_const(f)
    frac = sympify('0')
    for t in terms:
        de = fraction(t)[1]
        if de.is_real:
            a = t.coeff(x_symbol)
        else:
            frac = t
            c = de.coeff(x_symbol)
            d = symoms_and_const(de)[1]
    k = a / c
    de = fraction(frac)[1]
    cont = (b * c - a * d) / c
    return sympify('%s*(%s) + %s + %s' % (k, de, frac, cont), evaluate=False)


def sep_frac(frac_e, x_symbol='x'):
    """
     分离含有分式表达式的中的分式和其他项
    :param frac_e: 含有分式的表达式
    :param x_symbol: 未知变量
    :return:
    """
    frac_e = frac_simp(frac_e, x_symbol=x_symbol)
    mons = sympify('0')
    frac_mon = sympify('0')  # 分式
    symns, ct = symoms_and_const(frac_e)
    if not symns:
        return ct, None
    for sym in symns:
        d = fraction(sym)[1]
        if d.is_real:
            mons += sym
        else:
            frac_mon += sym
    mons = mons + ct
    return mons, frac_mon


def frac_psa_simp(e, x_symbol='x'):
    """
    将含分式的表达式转换为积定和表达式 最后转换为形如 k*g(x) + e/g(x) + c 备注:f是代表一个整体。
    假设 f = w(x) + e/g(x) 其中w(x)是含x的表达式, g(x)是分母表达式
    如果 只有 w(x) = k*g(x) + m(其中k, m为常数)的时候, 才能化简成积定和公式
    => w(x)/g(x) = k + m/g(x); m = w(x) - k*g(x) 备注: 因为在sympy中m可能是个分式,会进行化简,所以我们要用m = w(x) - k*g(x)进行求值;
    可以知道 k = (w(x)最高阶项的系数)/(g(x)最高阶的系数)
    所以只要保证 w(x)/g(x) 得到的k, m的结果为实数,即可化简成积定和公式
    :param e:  形如 w(x) + e/g(x)
    :param x_symbol: 未知变量
    :return:
    >>> frac_psa_simp('x + 1/(x - 2)')
    x - 2 + 2 + 1/(x - 2)
    >>> frac_psa_simp('(x**4 - x**2)/(x**2 + 1)')
    x**2 + 1 - 3 + 2/(x**2 + 1)
    >>> frac_psa_simp('(x**2 + x + 1)/x')
    x + 1 + 1/x
    >>> frac_psa_simp('3*x**2 + 5 + 1/(5*x**2 + 3)')
    3*(5*x**2 + 3)/5 + 16/5 + 1/(5*x**2 + 3)
    """
    e = sympify(e)
    f = frac_simp(e, x_symbol=x_symbol)  # 化简
    w, frac = sep_frac(f)
    g = fraction(frac)[1]  # 提取 分母。 一切都是按照分母来推理的。
    div = frac_simp(w / g)
    k, frac2 = sep_frac(div)
    m = w - k * g  # (m = fraction(frac2)[0] 这种方式不正确, 因为sympy有可能会进行化简)
    if k.is_real and m.is_real:  # 只有k 和 m都为常数的时候,才能进行积定和公式化简
        last_f = sympify('%s*(%s) + %s + %s' % (k, g, frac, m), evaluate=False)
        return last_f
    else:
        return f


def noneg_expr(e, symbol_interval):
    """
    表达式是否是一个非负数 >= 0
    :param e: 表达式
    :param symbol_interval: 变量取值范围
    :return:
    >>> noneg_expr('x**2 + 1', Interval(0, S.Infinity))
    True
    >>> noneg_expr('- x**2 - 2', Interval(0, S.Infinity))
    False
    """
    e = sympify(e)
    q = solveset(e >= 0, domain=S.Reals)
    if symbol_interval.is_subset(q):
        return True
    else:
        return False


def asp_ineq(e, symb_inls):
    """
    和定积公式(不等式)
    :param e: 和表达式
    :param symb_inls: 变量的取值范围
    :return: 恒成立的和定积不等式
    >>> asp_ineq('a**2 + 4*b', (('a', Interval(0, S.Infinity)), ('b', Interval(0, S.Infinity)))).printing()
    a^{2} + 4 b >= 4 a \\sqrt{b}
    >>> asp_ineq('a**2 + 4*b**2', (('a', Interval(0, S.Infinity)), ('b', Interval(0, S.Infinity)))).printing()
    a^{2} + 4 b^{2} >= 4 a b
    >>> asp_ineq('3**a + 3**b', (('a', Interval(0, S.Infinity)), ('b', Interval(0, S.Infinity)))).printing()
    3^{a} + 3^{b} >= 2 \\sqrt{3^{a + b}}
    """
    symb_inls = symb_inls if isinstance(symb_inls, dict) else dict(symb_inls)
    e = sympify(e)
    mons = map(lambda t: t[0] * t[1], e.as_coefficients_dict().items())
    sym_mons = filter(lambda mon: not mon.is_real, mons)
    cont = e - reduce(lambda m1, m2: m1 + m2, sym_mons)
    # 判断所有项是否都是正数
    for m in sym_mons:
        s = list(m.free_symbols)[0]
        inl = solveset(m >= 0, domain=S.Reals)
        if isinstance(inl, Reals):
            continue
        elif symb_inls[str(s)].is_subset(inl):
            continue
        else:
            return None
    r = 2 * sqrt(powsimp(reduce(lambda m1, m2: m1 * m2, sym_mons)))
    if 2 in degree_list(e):
        r = sqrtsimp(r, symb_inls.items())
    return BaseIneq([e, '>=', r + cont])


def frac_asp_ineq(frac_e, symb_inl=S.Reals, x_symbol='x'):
    """
    含有分式的表达式转换为和定积不等式
    :param frac_e: 含有分式的表达式
    :param symb_inl: 符号变量的取值范围
    :param x_symbol: 位置变量
    :return:
    >>> frac_asp_ineq('(x**4 + 9)/x**2').printing()
    x^{2} + \frac{9}{x^{2}} >= 6
    >>> frac_asp_ineq('(x**4 - x**2)/(x**2 + 1)').printing()
    x^{2} + 1 - 3 + \frac{2}{x^{2} + 1} >= -3 + 2 \\sqrt{2}
    >>> frac_asp_ineq('(x**2 + x + 1)/x', Interval(-S.Infinity, 0)).printing()
    x + 1 + \frac{1}{x} <= -1
    >>> frac_asp_ineq('x + 1/(x - 2)', Interval(2, S.Infinity)).printing()
    x - 2 + 2 + \frac{1}{x - 2} >= 4
    """

    def frac_expr_de(e):  # 含有分式表达式的分母
        for k, v in e.as_coefficients_dict().items():
            _, d = fraction(k)
            if not d.is_real:
                return d
        return fraction(cancel(e))[1]

    def sim_frac_asp_ineq(e):  # 最简单的形如 a*t + b/t 的形式 其中 a, b, t 都大于0
        sim_mons = symoms_and_const(e)[0]
        asp_value = reduce(lambda a, b: a * b, sim_mons)
        return BaseIneq([str(e), '>=', 2 * sqrt(asp_value)])

    de = fraction(cancel(frac_e))[1]
    subs_info = (de, 't')
    if noneg_expr(de, symb_inl):  # 如果是非负数
        t_pm = 1  # 代表正数
    else:
        t_pm = -1  # 代表负数
    frac_e = frac_psa_simp(frac_e, x_symbol)
    new_frac_e = frac_e.subs(subs_info[0], subs_info[1])
    if new_frac_e == frac_e:  # 没有变动
        n_d = frac_expr_de(frac_e)
        k_coeff = simplify(n_d / subs_info[0])
        new_frac_e = frac_e.subs(n_d, k_coeff * sympify(subs_info[1]))
    symons, const = symoms_and_const(new_frac_e)  # 分离积定和表达式的常数
    mon_coeff, frac_coeff = sympify('1'), sympify('1')
    for mon in symons:
        m_de = fraction(mon)[1]
        if m_de.is_real:
            mon_coeff = mon.as_coefficients_dict().values()[0]  # 项的系数
        else:
            frac_coeff = mon.as_coefficients_dict().values()[0]  # 分式的系数

    if mon_coeff * frac_coeff > 0:  # 同号的话,则可以化成积定和表达式
        new_frac_e_2 = sympify('0')
        if mon_coeff > 0:  # 如果系数大于0
            frac_pm = 1
        else:
            frac_pm = -1
        for mon in symons:
            tmp_mon = mon.as_coefficients_dict().keys()[0]
            tmp_coeff = abs(mon.as_coefficients_dict().values()[0])
            new_frac_e_2 += tmp_mon * tmp_coeff
        result_asp_ineq = sim_frac_asp_ineq(new_frac_e_2)
        left_hand, ineq_op, right_hand = result_asp_ineq.sympify()
        asp_ineq_interval = solveset('f' + ineq_op + str(right_hand), domain=S.Reals)
        last_interval = interval_mul_rel(asp_ineq_interval, t_pm * frac_pm)
        if last_interval.left == -S.Infinity:
            last_asp_ineq = BaseIneq([frac_e, '<=', last_interval.right + const])
        else:
            last_asp_ineq = BaseIneq([frac_e, '>=', last_interval.left + const])
        return last_asp_ineq
    else:
        return None


def real_2_log(r_value, base_value):
    """
    实数转换为log形式
    :param r_value: 实数
    :param base_value: log的底数
    :return:
    >>> real_2_log(3, 2)
    log(2**3)/log(2)
    """
    log_expr = 'log(%s**%s, %s)' % (str(base_value), str(r_value), str(base_value))
    return sympify(log_expr, evaluate=False)


def loged_expr(e):
    """
    判断是否是含有log的表达式
    :param e:
    :return:
    """
    e = sympify(e)
    mo, de = fraction(cancel(e))
    loged = False
    exprs = chain(mo.as_coefficients_dict().items(), de.as_coefficients_dict().items())
    for c, m in exprs:
        if isinstance(m, log) or isinstance(c, log):
            loged = True
    return loged


def log_ded_eq(log_eq):
    """
    由含有log的等式推出的不含有log的等式
    :param log_eq: 含有log的表达式
    :return:
    >>> log_ded_eq(BaseEq(['log(n)/log(m)', '-1'])).sympify()
    (n, 1/m)
    >>> log_ded_eq(BaseEq(['log(m)/log(3) + log(n)/log(3)', '4'])).sympify()
    (m*n, 81)
    """
    left, right = log_eq.sympify()
    f = left - right
    if not loged_expr(f):
        return log_eq
    _, const = symoms_and_const(f)
    new_left = f - const
    new_right = -const
    mo, de = fraction(cancel(new_left))
    if de == 1 and const == 0:  # log(x) + log(y) = 3
        mo_log = logcombine(new_left, force=True)
        return BaseEq([mo_log.args[0], 1])
    if not mo.is_real and not de.is_real:
        const_log = real_2_log(new_right, de.args[0])
        const_log_value = fraction(const_log)[0].args[0]
        const_log_value = const_log_value + 1 - 1
        return BaseEq([mo.args[0], const_log_value])
    elif not mo.is_real and de.is_real and isinstance(de, log):
        mo_log = logcombine(mo, force=True)
        const_log = real_2_log(new_right, de.args[0])
        if const_log == 1:
            const_log_value = de.args[0]
        else:
            const_log_value = fraction(const_log)[0].args[0]
        const_log_value = const_log_value + 1 - 1
        return BaseEq([mo_log.args[0], const_log_value])
    else:
        return log_eq


def right_const_eq(eq):
    """
    右边为常量的等式
    :param eq: 等式
    :return:
    >>> right_const_eq(BaseEq(['2*x + y - 3', '0'])).sympify()
    (2*x + y, 3)
    """
    f = eq_2_f(eq)
    _, const = symoms_and_const(f)
    new_left = f - const
    new_right = - const
    return BaseEq([new_left, new_right])


def interval_2_ineq_list(intl, e):
    """
    Interval类型转换为List类型
    :param intl: 
    :param e: 
    :return: 
    >>> interval_2_ineq_list(Interval(sympify('1/2'), 1), 'x')
    [['x', '>=', 1/2], ['x', '<=', 1]]
    """
    ineq_e_list = []
    if isinstance(intl, Interval):  # Interval类型
        if intl.left != -S.Infinity and intl.right != S.Infinity:
            ineq_e_list.append([e, ('>' if intl.left_open else '>='), intl.left])
            ineq_e_list.append([e, ('<' if intl.right_open else '<='), intl.right])
        elif intl.left == -S.Infinity:
            ineq_e_list.append([e, ('<' if intl.right_open else '<='), intl.right])
        else:
            ineq_e_list.append([e, ('>' if intl.left_open else '>='), intl.left])
    else:  # Union类型
        intls = intl.args
        for tmp_intl in intls:
            if tmp_intl.left != -S.Infinity and tmp_intl.right != S.Infinity:
                ineq_e_list.append([e, ('>' if tmp_intl.left_open else '>='), tmp_intl.left])
                ineq_e_list.append([e, ('<' if tmp_intl.right_open else '<='), tmp_intl.right])
            elif tmp_intl.left == -S.Infinity:
                ineq_e_list.append([e, ('<' if tmp_intl.right_open else '<='), tmp_intl.right])
            else:
                ineq_e_list.append([e, ('>' if tmp_intl.left_open else '>='), tmp_intl.left])
    return ineq_e_list


def simp_comp_fx(src_fx_str, x_interval, target_fx_str, x_symbol='x'):
    """
    已知f(x)表达式和x的取值范围，化简f(g(x))复合函数的表达式
    :param src_fx_str: f(x)的表达式
    :param x_interval: x的取值范围
    :param target_fx_str: 目标f(g(x))表达式
    :param x_symbol: 表示x的变量
    :return:
    >>> simp_comp_fx('x**2 + 4*x', Interval(0, S.Infinity), 'f(a**2 - 4)')
    (a**2*(a**2 - 4), (-oo, -2] U [2, oo), 'a')
    >>> simp_comp_fx('x**2 + 4*x', Interval(0, S.Infinity), 'f(3*a)')
    (3*a*(3*a + 4), [0, oo), 'a')
    """

    def interval_2_ineqs(intl, e):
        ineq_e_list = []
        # if isinstance(intl, Interval):  # Interval类型
        #     if intl.left != -S.Infinity and intl.right != S.Infinity:
        #         ineq_e_list.append([e, ('>' if intl.left_open else '>='), intl.left])
        #         ineq_e_list.append([e, ('<' if intl.right_open else '<='), intl.right])
        #     elif intl.left == -S.Infinity:
        #         ineq_e_list.append([e, ('<' if intl.right_open else '<='), intl.right])
        #     else:
        #         ineq_e_list.append([e, ('>' if intl.left_open else '>='), intl.left])
        # else:  # Union类型
        #     intls = intl.args
        #     for tmp_intl in intls:
        #         if tmp_intl.left != -S.Infinity and tmp_intl.right != S.Infinity:
        #             ineq_e_list.append([e, ('>' if tmp_intl.left_open else '>='), tmp_intl.left])
        #             ineq_e_list.append([e, ('<' if tmp_intl.right_open else '<='), tmp_intl.right])
        #         elif tmp_intl.left == -S.Infinity:
        #             ineq_e_list.append([e, ('<' if tmp_intl.right_open else '<='), tmp_intl.right])
        #         else:
        #             ineq_e_list.append([e, ('>' if tmp_intl.left_open else '>='), tmp_intl.left])
        # return ineq_e_list

        # 只考虑是Interval类型 不考虑Union类型
        if intl.left != -S.Infinity and intl.right != S.Infinity:
            ineq_e_list.append([str(e), ('>' if intl.left_open else '>='), str(intl.left)])
            ineq_e_list.append([str(e), ('<' if intl.right_open else '<='), str(intl.right)])
        elif intl.left == -S.Infinity:
            ineq_e_list.append([str(e), ('<' if intl.right_open else '<='), str(intl.right)])
        else:
            ineq_e_list.append([str(e), ('>' if intl.left_open else '>='), str(intl.left)])
        return ineq_e_list

    p = r'f\((.*)\)'
    f_p = r'f\(.*\)'
    m = re.search(p, str(target_fx_str))
    fx = re.findall(f_p, str(target_fx_str))[0]
    t = m.groups()[0]
    comp_x_symbol = str(list(sympify(t).free_symbols)[0])
    ineqs = interval_2_ineqs(x_interval, t)
    r_intl = Interval(-S.Infinity, S.Infinity)
    for ineq in ineqs:
        tmp_intl = solveset(''.join(ineq), domain=S.Reals)
        r_intl = r_intl.intersect(tmp_intl)
    r_fx = simplify(expand(sympify(src_fx_str).subs(x_symbol, t)))
    r_fx2 = sympify(target_fx_str).subs(fx, r_fx)
    return r_fx2, r_intl, comp_x_symbol


def seg_comp_fxs(seg_fxs, comp_fx):
    """
    分段复合函数
    :param seg_fxs: 分段函数
    :param comp_fx: 复合函数表达式
    :return:
    >>> seg_comp_fxs((('x**2 - 4*x + 3', 'x<=0'), ('-x**2 - 2*x + 3', 'x>0')), 'f(a**2 - 4) - f(3*a)')
    [(a**4 - 21*a**2 + 12*a + 32, [-2, 0]), (a**4 - 3*a**2 + 6*a + 32, (0, 2]), (-a**4 - 3*a**2 + 12*a - 8, (-oo, -2)), (-a**4 + 15*a**2 + 6*a - 8, (2, oo))]
    >>> seg_comp_fxs((('x**2 - 4*x + 3', 'x<=0'), ('-x**2 - 2*x + 3', 'x>0')), 'f(a**2 - 4) - f(3*a) + f(-1)')
    [(a**4 - 21*a**2 + 12*a + 40, [-2, 0]), (a**4 - 3*a**2 + 6*a + 40, (0, 2]), (-a**4 - 3*a**2 + 12*a, (-oo, -2)), (-a**4 + 15*a**2 + 6*a, (2, oo))]
    """

    def comped_fx(c_f):  # 是否是复合函数
        dig_p = r'f\(\d+\)'
        p = r'f\(.*\)'
        return re.findall(p, str(c_f)) and not re.findall(dig_p, str(c_f))

    def extr_comp_fxs(com_fx_e):  # 提取式子中的复合函数
        e = sympify(com_fx_e)
        c_f_terms = []
        for k, v in e.as_coefficients_dict().items():
            if comped_fx(k):  # 如果是复合函数
                c_f_terms.append(v * k)
        return c_f_terms

    def replace_dig_comp_fxs(seg_fxs2, comp_fx_e):  # 替换掉
        e = sympify(comp_fx_e)
        new_e = e
        dig_p = r'f\(-?\s*\d*\)'
        dig_r_p = r'f\((-?\s*\d*)\)'
        for k, v in e.as_coefficients_dict().items():
            ts = re.findall(dig_p, str(k))
            if ts:
                dig_v = sympify(re.search(dig_r_p, str(k)).groups()[0])
                dig_f = ts[0]
                for f2, i2 in seg_fxs2:
                    x_sy = list(sympify(f2).free_symbols)[0]
                    if dig_v in ineq_expr_list_2_interval(to_ineq_expr_list(i2)):
                        r_f = sympify(f2).subs(x_sy, dig_v)
                        new_e = new_e.subs(dig_f, r_f)
        return new_e

    tmp_seg_fxs = []
    x_symbol = 'x'
    # target_symbol = list(sympify(comp_fx).free_symbols)[0]
    for f, intl_str in seg_fxs:
        if sympify(f).free_symbols:
            x_symbol = list(sympify(f).free_symbols)[0]
        tmp_seg_fxs.append((f, ineq_expr_list_2_interval(to_ineq_expr_list(intl_str))))
    comp_fx = replace_dig_comp_fxs(seg_fxs, comp_fx)
    c_fs = extr_comp_fxs(comp_fx)
    seg_comps = combin_iters(c_fs, tmp_seg_fxs)
    new_seg_fxs = map(lambda s: (s[0], simp_comp_fx(s[1][0], s[1][1], s[0], x_symbol)), seg_comps)
    seg_fx_len = len(seg_fxs)
    comps = nh_zip(new_seg_fxs, seg_fx_len)
    result_seg_fxs = combin_iters(*comps)

    last_seg_fxs = []
    for tmp_seg in result_seg_fxs:
        intls = map(lambda s: s[1][1], tmp_seg)
        r_i = intervals_intersect(intls)
        tmp_comp_fx = sympify(comp_fx)
        for comp_f, sub_info in tmp_seg:
            tmp_comp_fx = tmp_comp_fx.subs(comp_f, sub_info[0])
        last_seg_fxs.append((tmp_comp_fx, r_i))
    return last_seg_fxs


def solve_poly_eqs(eqs):
    """
    解多元方程组
    :param eqs: 方程组
    :return:
    >>> solve_poly_eqs(BaseEqs([['x + y', '5'], ['x - y', '3']]))
    [[(x, 4), (y, 1)]]
    >>> solve_poly_eqs(BaseEqs([['x + y', '5'], ['x - y', '3'], ['z + y', '5']]))
    [[(x, 4), (y, 1), (z, 4)]]
    """
    eqs_list = to_base_list(eqs)
    eq_polys = list(map(lambda eq: eq[0] - eq[1], eqs_list))
    symbs = list(map(lambda ef: ef.free_symbols, eq_polys))
    symbs = sorted(set(flatten(symbs)), key=str)
    roots = solve_poly_system(eq_polys, *symbs)
    return list(map(lambda root: list(zip(symbs, root)), roots))


def ineqs_symbs(ineqs):
    """
    不等式列表中的符号变量
    :param ineqs:
    :return:
    >>> ineqs_symbs(['3', '<', 'x', '<', 'y', '<', '3'])
    ['x', 'y']
    >>> ineqs_symbs([['3', '<', 'x', '<', 'y', '<', '3'], ['2', '<', 'z', '<', '3']])
    ['x', 'y', 'z']
    """
    ineqs_fac = list(filter(lambda a: str(a) not in ['<=', '<', '>=', '>'], flatten(ineqs)))
    ineqs_fac = list(map(sympify, ineqs_fac))
    symbs = list(map(lambda f: list(f.free_symbols), ineqs_fac))
    symbs = flatten(symbs)
    symbs = FiniteSet(*symbs)
    return sorted(list(map(str, list(symbs))))


def eqs_symbs(eqs):
    """
    等式中的变量
    :param eqs: 
    :return: 
    >>> eqs_symbs([['sin(x)', '1']])
    ['x']
    """
    eq_2_ineqs = list(map(lambda _: [_[0], '>', _[1]], eqs))
    return ineqs_symbs(eq_2_ineqs)


def comp_func(f_expr, comp_f, x_symbol='x', f_symbol='f'):
    """
    f的复合函数
    :param f_expr: 函数表达式
    :param comp_f: 复合函数表达式
    :param x_symbol: 函数表达式中的未知变量
    :param f_symbol: 复合表达式中的函数未知变量
    :return:
    >>> comp_func('(x+2)**2 + 1', 'f(a)')
    (a + 2)**2 + 1
    >>> comp_func('(x+2)**2 + 1', 'f(f(a) + 2)')
    ((a + 2)**2 + 5)**2 + 1
    >>> comp_func('(x+2)**2 + 1', 'f(a + f(b + f(c)))')
    (a + (b + (c + 2)**2 + 3)**2 + 3)**2 + 1
    """
    f_expr, comp_f = sympify(f_expr), sympify(comp_f)
    f_arg = comp_f.args[0]
    coefs_dict = coefficients_dict(f_arg)
    x_sub = sympify('0')
    for m, c in coefs_dict.items():
        if not str(type(m)) == f_symbol:
            x_sub += m * c
        else:
            x_sub += c * comp_func(f_expr, m, x_symbol, f_symbol)
    return f_expr.subs(x_symbol, x_sub)


def search_fxs(expr, deep=False):
    """
    搜寻表达式中的所有的f(x)表达式
    :param expr:
    :param deep: 是否递归的搜寻
    :return:
    >>> search_fxs(sympify('3*x + f(2*x) + f(3*x + 5 + f(b + 2))'))
    ['f(2*x)', 'f(3*x + f(b + 2) + 5)']
    >>> search_fxs(sympify('3*x + f(2*x) + f(3*x + 5 + f(b + 2))'), deep=True)
    ['f(2*x)', 'f(3*x + f(b + 2) + 5)', 'f(b + 2)']
    """

    def able_fx(full_expr, start_fx, start_i):
        full_expr = str(full_expr)
        start_fx = str(start_fx)
        end_i = full_expr.find(start_fx, start_i) + len(start_fx)
        expr_size = len(full_expr)
        while end_i <= expr_size:
            last_fx = full_expr[start_i:end_i]
            try:
                sympify(last_fx)
                break
            except Exception:
                pass
            end_i += 1
        return last_fx

    expr = str(expr)
    p = r'f\(.*?\)'
    fx_list = re.findall(p, expr)
    fxs = []
    start_find = 0
    for tmp_fx in fx_list:
        start_find = expr.find(tmp_fx, start_find)
        fx = able_fx(expr, tmp_fx, start_find)
        if deep:
            if re.findall(p, str(fx)[1:]):
                fxs.append(str(fx))
                fxs.extend(search_fxs(str(fx)[1:]))
            else:
                fxs.append(str(fx))
        else:
            fxs.append(str(fx))
        start_find += len(str(fx))
    fxs = filter(lambda f_e: f_e.find('f(') >= 0, fxs)
    return fxs


def base_eqs_symbs(base_eqs):
    """
    等式中有哪些变量
    :param base_eqs:
    :return:
    """
    eqs_list = to_base_list(base_eqs)
    symbs = []
    for l_expr, r_expr in eqs_list:
        eq_f = l_expr - r_expr
        symbs.extend(list(eq_f.free_symbols))
    symbs = sorted(map(str, set(symbs)))
    return map(sympify, symbs)


def base_set_2_sympy_set(base_set):
    """
    BaseSet转换为sympy的Interval或者Union
    :param base_set:
    :return:
    """
    ineqs = filter(lambda cond: 'ineq' in cond, base_set.value['conditions'])
    ineqs_values = list(map(lambda ineq: ineq['ineq'], ineqs))
    if len(ineqs_values) == 1 and len(ineqs_values[0]) == 5:
        currnt_ineq = ineqs_values[0]
        s1 = solveset(''.join(currnt_ineq[:3]), domain=S.Reals)
        s2 = solveset(''.join(currnt_ineq[2:]), domain=S.Reals)
        return s1.intersect(s2)
    else:
        ineq_intls = []
        for ineq_value in ineqs_values:
            ineq_intls.append(solveset(''.join(ineq_value), domain=S.Reals))
        return intervals_union(ineq_intls)


def search_value(base_obj, key):
    """
    self.search方法
    :param base_obj:
    :param key:
    :return:
    """
    v = base_obj.search(key)
    if isinstance(v, FiniteSet):
        return list(v)
    else:
        return v


def convert_eq_no_den(eq):
    """
    转换等式，等式两边都不含有分母
    :param eq:
    :return:
    >>> convert_eq_no_den(BaseEq(['x/y', '3'])).sympify()
    [x, 3*y]
    """
    l, r = eq.sympify()
    l_mo, l_den = complete_fraction(l)
    r_mo, r_den = complete_fraction(r)
    new_eq = BaseEq([l_mo * r_den, r_mo * l_den])
    return new_eq


def ichain(*iter_list):
    """
    链接所有列表
    :param iter_list:
    :return:
    >>> list(ichain([1, 2, 3, 4], [5, 6], [7, 8, 9]))
    [1, 2, 3, 4, 5, 6, 7, 8, 9]
    """
    return reduce(lambda _1, _2: chain(_1, _2), iter_list)


def _mathematic_sub(m_expr):
    s_expr = m_expr.subs('Pi', pi)
    return s_expr


def mm_expr_sub(m_expr):
    """
    去掉mathematic 中特殊的字符
    :param m_expr: 
    :return: 
    """
    s_expr = m_expr.subs('Pi', pi).subs('C_1', 'k')
    return s_expr


def simplify_value(compl_value):
    """
    化简复杂的值
    :param compl_value: 
    :return: 
    >>> simplify_value('sqrt(3**2)')
    3
    """
    return MathematicaSimplify().solver(BaseNumber(compl_value)).output[0].value


def isolve_eq(eq, x_symbol='x'):
    """
    使用mathematic解方程
    :param eq:
    :param x_symbol:
    :return:
    >>> isolve_eq(BaseEq(['2*k + pi', 'x']))
    {x: {2*k + pi}}
    """
    l, r = eq.sympify()
    f = l - r
    symbs = list(f.free_symbols)
    if len(symbs) == 1:
        x_symbol = symbs[0]
    m_solve = MathematicaSolve().solver(eq, BaseVariables([x_symbol])).output[0].sympify()
    return dict(map(lambda pair: (pair[0], FiniteSet(*map(lambda _: _.subs('Pi', pi), list(pair[1])))),
                    m_solve.items()))


def isolve_eq2(eq, x_symbol='x'):
    """
    解方程
    :param eq: 
    :param x_symbol: 
    :return: 
    >>> isolve_eq2(BaseEq(['k - 2', 'y']), 'w')
    [y + 2]
    >>> isolve_eq2(BaseEq(['Abs(y)', '1']), 'y')
    [-1, 1]
    """
    solve_set = isolve_eq(eq, x_symbol)
    return list(list(solve_set.items())[0][1])


def isolve_eqs(eqs, variables=None):
    """
    使用mathematic解方程组
    :param eqs: 方程组
    :param variables: 方程
    :return: 所有可能的方程组解, 按照参数中变量的顺序输出
    >>> isolve_eqs(BaseEqs([[2 * pi, '2*pi/Abs(w)'], ['w*pi/4 + a', 'k*pi +pi/2']]), ['w', 'a'])
    [[(w, -1), (a, pi*k + 3*pi/4)], [(w, 1), (a, pi*k + pi/4)]]
    >>> isolve_eqs(BaseEqs([[2 * pi, '2*pi/Abs(w)'], ['w*pi/4 + a', 'k*pi +pi/2']]), ['a', 'w'])
    [[(a, pi*k + 3*pi/4), (w, -1)], [(a, pi*k + pi/4), (w, 1)]]
    >>> isolve_eqs(BaseEqs([['x+y', '10'], ['x*y', '25']]), ['x', 'y'])
    [[(x, 5), (y, 5)]]
    >>> isolve_eqs(BaseEqs([['x+10', '20']]), ['x'])
    [[(x, 10)]]
    >>> isolve_eqs(BaseEqs([['Abs(w)', '1']]), ['w'])
    [[(w, -1)], [(w, 1)]]
    >>> isolve_eqs(BaseEqs([['x2 - 3*pi/4', 0], ['x0 + x1 - pi', 0]]), ['x0', 'x1', 'x2']) # 未知变量个数多余方程的个数
    [[(x1, -x0 + pi), (x2, 3*pi/4)]]
    """
    if not variables:
        if isinstance(eqs, BaseEq):
            variables = eqs_symbs([eqs.sympify()])
        else:
            variables = eqs_symbs(eqs.sympify())
    variables = list(map(sympify, list(flatten([variables]))))
    variables_subs = []
    result_subs = []
    new_vars = []
    for index, symb in enumerate(variables):
        n_s = 'u' + str(index)
        n_s = sympify(n_s)
        variables_subs.append((symb, n_s))
        result_subs.append((n_s, symb))
        new_vars.append(sympify(n_s))
    if isinstance(eqs, BaseEqs):
        new_eqs = list(map(lambda _: [_[0].subs(variables_subs), _[1].subs(variables_subs)], eqs.sympify()))
    else:
        new_eqs = list(map(lambda _: [_[0].subs(variables_subs), _[1].subs(variables_subs)], [eqs.sympify()]))
    solve_set = MathematicaSolve().solver(BaseEqs(new_eqs), BaseVariables(new_vars)).output[0].value
    if not solve_set:  # 如果解集是空集
        return EmptySet()
    vars_key, vars_results = list(solve_set.items())[0]
    vars_key = list(flatten([vars_key]))
    results = []
    for result_pair in vars_results:
        symb_result_pairs = map(lambda _: _mathematic_sub(_), list(flatten([result_pair])))
        one_syms_set = dict(zip(vars_key, symb_result_pairs))
        vars_set = map(lambda v: (v.subs(result_subs),
                                  one_syms_set.get(v, None).subs(result_subs) if one_syms_set.get(v, None) is not None
                                  else None), new_vars)
        vars_set = list(filter(lambda _: _[1] is not None, vars_set))
        results.append(vars_set)
    return results


def syms_set_to_single_dict(symbs_values):
    """
    将解集转换为每个字母为键字典形式
    :param symbs_values: 
    :return: 
    >>> syms_set_to_single_dict([[('a', 'pi * k + 3 * pi / 4'), ('w', -1)], [('a', 'pi * k + pi / 4'), ('w', 1)]])
    {'a': ['pi * k + 3 * pi / 4', 'pi * k + pi / 4'], 'w': [-1, 1]}
    """
    result_dict = {}
    for one_symbs_set in symbs_values:
        for symb, symb_value in one_symbs_set:
            sym_values = result_dict.get(symb, [])
            sym_values.append(symb_value)
            result_dict[symb] = sym_values
    return result_dict


def syms_set_to_tuple_dict(symbs_values):
    """
    将解集转换为字母组为键字典形式
    :param symbs_values: 
    :return: 
    >>> syms_set_to_tuple_dict([[('a', 'pi * k + 3 * pi / 4'), ('w', -1)], [('a', 'pi * k + pi / 4'), ('w', 1)]])
    {('a', 'w'): [('pi * k + 3 * pi / 4', -1), ('pi * k + pi / 4', 1)]}
    """
    result_dict = {}
    for one_symbs_set in symbs_values:
        syms_tuple, values_tuple = zip(*one_symbs_set)
        syms_values = result_dict.get(syms_tuple, [])
        syms_values.append(values_tuple)
        result_dict[syms_tuple] = syms_values
    return result_dict


def single_dict_2_tuple_dict(single_dict):
    """
    单个键值的字典 转换为键值组的字典
    :param single_dict: 
    :return: 
    >>> single_dict_2_tuple_dict({'a': ['pi * k + 3 * pi / 4', 'pi * k + pi / 4'], 'w': [-1, 1]})
    {('a', 'w'): [('pi * k + 3 * pi / 4', -1), ('pi * k + pi / 4', 1)]}
    """
    single_key_value_items = single_dict.items()
    tuple_key, tuple_values = zip(*single_key_value_items)
    tuple_values = zip(*tuple_values)
    return dict({tuple_key: tuple_values})


def tuple_dict_2_single_dict(tuple_dict):
    """
    与single_dict_2_tuple_dict相反操作
    :param tuple_dict: 
    :return: 
    >>> tuple_dict_2_single_dict({('a', 'w'): [('pi * k + 3 * pi / 4', -1), ('pi * k + pi / 4', 1)]})
    {'a': ['pi * k + 3 * pi / 4', 'pi * k + pi / 4'], 'w': [-1, 1]}
    """
    result_dict = {}
    tuple_key, tuple_values = list(tuple_dict.items())[0]
    for tuple_value in tuple_values:
        key_values = zip(tuple_key, tuple_value)
        for key, value in key_values:
            key_values = result_dict.get(key, [])
            key_values.append(value)
            result_dict[key] = key_values
    return result_dict


def is_always_hold(ineq_expr, symbs_interval):
    """
    不等式是否恒成立
    :param ineq_expr: 不等式 
    :param symbs_interval: 不等式中符号的取值范围
    :return: 
    >>> is_always_hold('4*a + 6*b > 2*b',{'a': Interval(0, S.Infinity, left_open=True), 'b': Interval(0, S.Infinity, left_open=True)})
    True
    """
    symbs_interval = dict(map(lambda _: (sympify(_[0]), _[1]), symbs_interval.items()))
    _ineqs = []
    for _symb, _intl in symbs_interval.items():
        if isinstance(_intl, Interval):
            _ineq = interval_2_ineq_list(_intl, _symb)
            _ineqs.extend(_ineq)
    _ineqs.append(to_ineq_expr_list(ineq_expr))
    ineq_set = MathematicaSolve().solver(BaseIneqs(_ineqs)).output[0].value
    symbs_set_list = []
    for _sk, _intls_list in ineq_set.items():
        if _intls_list == EmptySet():
            return False
        for _intls in _intls_list:
            _set = zip(_sk, _intls)
            symbs_set_list.append(_set)
    for seg_set in symbs_set_list:
        for s, intl in seg_set:
            if intl.is_real:
                intl = FiniteSet(intl)
            if not symbs_interval[s].is_subset(intl):
                return False
    return True


def remove_param_abs_expr(e):
    """
    去掉绝对值中含参的表达式,可以化成的式子.Note:绝对值不会嵌套的
    :param e: 
    :return: 
    >>> remove_param_abs_expr('Abs(x - a) + Abs(x - b) + 3')
    [a + b - 2*x + 3, a - b + 3, -a + b + 3, -a - b + 2*x + 3]
    """
    _e = sympify(e)
    abs_mons = find_reg_expr('Abs\\(.*?\\)', _e)
    np_mons = map(lambda _: ((sympify(_), sympify(_).args[0]), (sympify(_), -sympify(_).args[0])), abs_mons)
    exprs = combin_iters(*np_mons)
    last_exprs = []
    for ce in exprs:
        _expr = sympify(str(_e))
        for se, te in ce:
            _expr = _expr.subs(str(se), str(te))
        last_exprs.append(_expr)
    last_exprs = list(set(last_exprs))
    return last_exprs


def remove_abs_ineqs(ineqs):
    """
    去掉不等式中含有绝对值的
    :param ineqs: 
    :return: 
    """
    ineqs_list = ineqs.sympify()
    new_ineqs = []
    for _l, _op, _r in ineqs_list:
        _f = _l - _r
        for f1 in remove_param_abs_expr(_f):
            new_ineqs.append([f1, _op, '0'])
    return BaseIneqs(new_ineqs)


def i_simp_ineqs(ineqs):
    """
    彻底化简各种隐含的不等式组[含绝对值，含分式]
    :param ineqs: 
    :return: 
    """
    ineqs1 = remove_abs_ineqs(ineqs)
    ineqs2 = simp_ineqs(ineqs1)
    return ineqs2


def point_print(point):
    """
    打印坐标
    :param point: 
    :return: 
    """
    return '(' + new_latex(point[0]) + ',' + new_latex(point[1]) + ')'


def i_subs(f, subs):
    """
    sympy的subs方法有时候会有问题
    :param f: 
    :param subs: 
    :return: 
    """
    f = sympify(str(f))
    return f.subs(subs)


def i_solve(f, symbs=None, symbs_domains=None):
    """
    更完善的solve 
    :param f: 代数表达式, BaseEq, BaseEqs
    :param symbs: 符号
    :param symbs_domains: 符号的区域
    :return: 
    """
    from mathsolver.functions.sanjiao import sanjiao_utils as su
    if is_algebraic_expr(f):  # 如果是代数表达式
        if not symbs:  # 如果没有指定变量
            isolve_eq2()
            pass
        if su.is_trig_f(f):  # 如果是三角表达式
            pass
        else:
            pass
    elif isinstance(f, BaseEq):  # 如果是等式
        pass
    elif isinstance(f, BaseEqs):  # 如果是等式组
        pass
    else:
        raise Exception('Illegal parameter')


if __name__ == '__main__':
    pass
